Skip to main content
CoreSDK
Getting Started

Python Quickstart

Add CoreSDK to a FastAPI, Flask, or Django service.

Python Quickstart

Prerequisites

  • Python 3.10+
  • A running coresdk-sidecar (or set fail_mode = "open" to develop without one)

Install

# FastAPI
pip install "coresdk[fastapi]" uvicorn

# Flask
pip install "coresdk[flask]" gunicorn

# Django
pip install "coresdk[django]"

# All frameworks
pip install "coresdk[fastapi,flask,django]"

Configure

Create coresdk.toml in your project root:

[sdk]
sidecar_addr  = "[::1]:50051"
tenant_id     = "acme-corp"
service_name  = "my-api"
fail_mode     = "open"      # "open" = allow on sidecar error; "closed" = deny
dev_mode      = true

[observability]
service_version = "0.1.0"
otel_endpoint   = ""        # set to http://localhost:4317 for trace export

[[tenants.acme-corp]]
display_name = "Acme Corp"

All [sdk] keys can be overridden with CORESDK_* env vars:

CORESDK_SIDECAR_ADDR=[::1]:50051 \
CORESDK_TENANT_ID=acme-corp \
CORESDK_FAIL_MODE=closed \
uvicorn main:app

Initialize the SDK

import os, tomllib
from pathlib import Path
from coresdk import CoreSDKClient, SDKConfig

# Load from coresdk.toml (with env var overrides)
with open("coresdk.toml", "rb") as f:
    cfg = tomllib.load(f)

sdk_cfg = cfg["sdk"]
_sdk = CoreSDKClient(SDKConfig(
    sidecar_addr = os.getenv("CORESDK_SIDECAR_ADDR", sdk_cfg["sidecar_addr"]),
    tenant_id    = os.getenv("CORESDK_TENANT_ID",    sdk_cfg["tenant_id"]),
    service_name = os.getenv("CORESDK_SERVICE_NAME", sdk_cfg["service_name"]),
    fail_mode    = os.getenv("CORESDK_FAIL_MODE",    sdk_cfg["fail_mode"]),
))

Or load entirely from env vars:

from coresdk import CoreSDKClient, SDKConfig
_sdk = CoreSDKClient(SDKConfig.from_env())

FastAPI

Add middleware

from fastapi import FastAPI, Request
from coresdk.middleware.fastapi import CoreSDKMiddleware

class SDKAdapter:
    config = _sdk.config
    def authorize_sync(self, token, **kw):
        return _sdk.validate_token(token, **kw)

app = FastAPI()
app.add_middleware(
    CoreSDKMiddleware,
    sdk=SDKAdapter(),
    exclude_paths=["/healthz", "/docs", "/openapi.json"],
)

Read claims in your routes

from fastapi import Request

@app.get("/me")
async def me(request: Request):
    # CoreSDKMiddleware sets request.state.coresdk_user after auth
    user = getattr(request.state, "coresdk_user", None) or {}
    return {"sub": user.get("sub"), "tenant": user.get("tenant_id")}

Role-based access control

from fastapi import Depends, HTTPException, Request

def require_role(role: str):
    def _check(request: Request):
        user = getattr(request.state, "coresdk_user", None) or {}
        if role not in user.get("roles", []):
            raise HTTPException(status_code=403, detail={
                "type":   "https://coresdk.io/errors/forbidden",
                "title":  "Forbidden",
                "status": 403,
                "detail": f"Role '{role}' required",
            })
        return user
    return Depends(_check)

@app.post("/products")
async def create_product(body: dict, _=require_role("editor")):
    ...

@app.delete("/products/{id}")
async def delete_product(id: int, _=require_role("admin")):
    ...

Multi-tenant isolation

def get_tenant(request: Request) -> str:
    user = getattr(request.state, "coresdk_user", None) or {}
    return user.get("tenant_id") or "default"

@app.get("/products")
async def list_products(request: Request):
    tenant = get_tenant(request)
    return db.products.for_tenant(tenant)   # always scoped to caller's tenant

Rego policy evaluation (ABAC)

@app.get("/documents/{doc_id}")
async def get_document(doc_id: str, request: Request):
    user   = getattr(request.state, "coresdk_user", None) or {}
    tenant = get_tenant(request)
    doc    = db.get_document(doc_id, tenant)

    allowed = _sdk.evaluate_policy("data.authz.allow", {
        "tenant_id":      tenant,
        "subject":        user.get("sub"),
        "action":         "read",
        "resource":       f"documents/{doc_id}",
        "resource_owner": doc["owner"],
        "context":        {"roles": user.get("roles", [])},
    })
    if not allowed:
        raise HTTPException(status_code=403, detail={
            "type":  "https://coresdk.io/errors/forbidden",
            "title": "Forbidden", "status": 403,
        })
    return doc

PII-safe tracing

from coresdk.tracing.decorator import trace

@app.get("/products")
@trace(intent="list-products")       # creates OTel span; PII scrubbed before export
async def list_products(request: Request):
    ...

RFC 9457 error handler

from fastapi.responses import JSONResponse
from coresdk.errors._rfc9457 import ProblemDetailError

@app.exception_handler(ProblemDetailError)
async def problem_detail_handler(request: Request, exc: ProblemDetailError):
    return JSONResponse(
        status_code=exc.status,
        content=exc.to_dict(),
        media_type="application/problem+json",
    )

Flask

from flask import Flask, g
from coresdk.middleware.flask import CoreSDKFlask
from coresdk.tracing.decorator import trace

app = Flask(__name__)
CoreSDKFlask(_sdk, app)    # registers before_request hook + ProblemDetailError handler

@app.get("/me")
@trace(intent="get-current-user")
def me():
    # Flask middleware stores claims on flask.g.claims
    return (g.claims or {"sub": "anonymous"}), 200

@app.post("/products")
def create_product():
    user = g.claims or {}
    if "editor" not in user.get("roles", []):
        return {"type": "https://coresdk.io/errors/forbidden",
                "title": "Forbidden", "status": 403}, 403, \
               {"Content-Type": "application/problem+json"}
    ...

Django

# settings.py
MIDDLEWARE = [
    "coresdk.django.CoreSDKMiddleware",
    "django.middleware.common.CommonMiddleware",
    # ...
]
CORESDK = {
    "sidecar_addr": "[::1]:50051",
    "tenant_id":    "acme-corp",
    "fail_mode":    "open",
    "service_name": "my-django-api",
}
# views.py
from django.http import JsonResponse
from coresdk.tracing.decorator import trace

@trace(intent="get-current-user")
def me(request):
    # Django middleware stores claims on request.coresdk_claims
    claims = getattr(request, "coresdk_claims", None) or {}
    return JsonResponse({"sub": claims.get("sub"), "tenant": claims.get("tenant_id")})

def require_role(role):
    def decorator(view):
        def wrapper(request, *args, **kwargs):
            claims = getattr(request, "coresdk_claims", None) or {}
            if role not in claims.get("roles", []):
                return JsonResponse({
                    "type": "https://coresdk.io/errors/forbidden",
                    "title": "Forbidden", "status": 403,
                }, status=403, content_type="application/problem+json")
            return view(request, *args, **kwargs)
        return wrapper
    return decorator

Where claims live by framework

FrameworkClaims location
FastAPIrequest.state.coresdk_user
Flaskflask.g.claims
Djangorequest.coresdk_claims

Full working examples

On this page