weldall-sdk
A Python service should not read data merely because an agent can reach its API.
weldall-sdk checks the employee or machine behind each request before application
code runs. It also serves the OAuth routes that connect the service to Weldall and
can publish instructions for agents.
The package follows the same protocol as the TypeScript @weldall/sdk. It includes
a framework-neutral core and adapters for FastAPI and Django.
Install the SDK
The SDK requires Python 3.11 or newer.
pip install weldall-sdk # framework-neutral core
pip install 'weldall-sdk[fastapi]' # FastAPI adapter
pip install 'weldall-sdk[django]' # Django adapter
Protect requests without a framework
Create one SDK instance when the service starts. The example generates a temporary key and keeps replay data in memory. Use both choices only during development.
from weldall import Request, generate_es256_key_pair, in_memory, init_weldall
key = generate_es256_key_pair()
weldall = init_weldall(
"https://weldall.example.com",
{
"resource": "https://contracts.example.com/api",
"public_origin": "https://contracts.example.com",
"client_id": "weldall-cli-at-contracts",
"supported_scopes": ["contracts:read"],
"signing_key": {
"kid": "development-only",
"private_jwk": key["private_jwk"],
"public_jwk": key["public_jwk"],
},
"replay_store": in_memory(),
},
)
weldall.ready() # optional fail-fast discovery and signing-key validation
auth = weldall.verify(
Request("GET", public_url, request_headers),
{"scopes": ["contracts:read"]},
)
print(auth.identity.type, auth.subject, auth.scopes)
verify() raises WeldallAuthError. verify_no_throw() instead returns a
VerifyResult containing either auth, or error and a framework-neutral OAuth
Response. Every scope in scopes is required; when any_scopes is non-empty,
at least one of those scopes is required too. An empty policy still authenticates
the request.
The handlers are:
| Route | Handler |
|---|---|
GET /.well-known/oauth-authorization-server |
handlers.authorization_server_metadata |
GET /.well-known/oauth-protected-resource |
handlers.protected_resource_metadata |
GET /.well-known/oauth-protected-resource/* |
handlers.protected_resource_metadata |
GET /.well-known/jwks.json |
handlers.jwks |
GET /.well-known/weldall-skills |
handlers.skills (when configured) |
POST /oauth/token |
handlers.token |
Protect FastAPI routes
from fastapi import Depends, FastAPI, Request
from weldall.adapters.fastapi import init_weldall
weldall = init_weldall("https://weldall.example.com", options)
app = FastAPI()
weldall.register_routes(app)
@app.get("/api/contracts")
def contracts(
request: Request,
auth=Depends(weldall.require_auth({"scopes": ["contracts:read"]})),
):
# The dependency also stores the same value on request.state.
assert weldall.get_auth(request) == auth
return {"requested_by": auth.subject, "contracts": []}
register_routes() mounts the complete protocol surface and installs the exception
handler that preserves the core OAuth body. If an application uses
require_auth() without registering protocol routes, call
weldall.install_exception_handler(app) once.
Protect Django paths
The adapter provides protocol views, URL patterns, and middleware for selected
paths. Include weldall.urls() in the root URL configuration.
from django.urls import include, path
from weldall.adapters.django import init_weldall
weldall = init_weldall("https://weldall.example.com", options)
urlpatterns = [path("", include(weldall.urls()))]
Wrap protected views directly, or construct the same middleware from a Django middleware factory:
protected = weldall.middleware(
application_view,
protected_paths=["/api"],
policy={"scopes": ["contracts:read"]},
)
# Inside application_view after middleware succeeded:
auth = weldall.get_auth(request)
The constructor form is
WeldallMiddleware(get_response, weldall, protected_paths=[...], policy=...).
This explicit instance injection avoids global SDK state and lets Django projects
choose one SDK instance per resource configuration.
Call another service as a machine
import os
from weldall import (
calculate_jwk_thumbprint,
load_es256_key_pair_from_env,
request_machine_token,
)
private_jwk, public_jwk = load_es256_key_pair_from_env(
"MACHINE_SIGNING_PRIVATE_JWK",
os.environ.get("MACHINE_SIGNING_PRIVATE_JWK"),
"MACHINE_SIGNING_PUBLIC_JWK",
os.environ.get("MACHINE_SIGNING_PUBLIC_JWK"),
)
key = {
"private_jwk": private_jwk,
"public_jwk": public_jwk,
"jkt": calculate_jwk_thumbprint(public_jwk),
}
result = request_machine_token(
issuer="https://weldall.example.com",
client_id="expenses-a",
resource="https://expenses-b.example.com/api",
scopes=["expenses-b:read"],
kid=os.environ["MACHINE_SIGNING_KID"],
key=key,
)
The result uses OAuth/Python snake-case keys: access_token, token_type,
expires_in, and scope. The normal resource verify() method accepts both user
at+jwt tokens and weldall-machine+jwt tokens and returns a discriminated
auth.identity.type.
Publish instructions for agents
A service can publish the commands that an agent needs for its API. Configure
exactly one static items list or synchronous load callable:
"skills": {
"items": [
{
"id": "list",
"title": "List contracts",
"requiredScopes": ["contracts:read"],
"visibility": "HIDDEN_IF_UNALLOWED",
"content": "# List contracts\n\nUse the contracts API.",
"meta": {"tags": ["contracts"], "owner": "Legal Operations"},
}
]
}
Catalogs are strictly validated, limited to 100 skills and 1 MiB, and require replay protection because Weldall fetches them with one-use signed assertions.
Use managed signing keys
A direct signing_key contains kid, private_jwk, and public_jwk. A service
that keeps keys in a KMS or Vault can instead provide synchronous current() and
jwks() methods.
current() returns a key with kid, public_jwk, and
sign(payload, protected_header). Before returning any token, the SDK verifies
that the active key is published, verifies the returned ES256 signature and exact
header, and deep-compares the returned claims to the requested claims.
Where the Python API differs
Protocol values, validation order, claims, errors, cache behavior, and replay
behavior match @weldall/sdk. The language/framework-only differences are:
- Public functions, fields, and option keys use
snake_case; wire-format JWT, OAuth, and skill-catalog keys remain unchanged. - The core, replay store, discovery, machine client, and signing-provider contracts
are synchronous.
httpx.Clientis injectable ashttp_clientfor the machine client; FastAPI only awaits request-body collection before calling the sync core. Request,Response, and the discriminatedVerifyResultdataclasses replace the JavaScript Fetch classes. Registry normalization returns canonical URL strings instead of JavaScriptURLobjects.load_es256_key_pair_from_env()returns(private_jwk, public_jwk); generated DPoP key pairs use a typed mapping withprivate_jwk,public_jwk, andjkt.- FastAPI stores auth in
request.state.weldall_auth; Django stores it asrequest.weldall_auth. Django middleware receives the configured SDK instance explicitly rather than relying on process-global settings. - Framework registration APIs naturally follow FastAPI/Django routing conventions; JavaScript-only Hono, Next.js, and Astro adapters are not reproduced.
Prepare a production service
- Load a stable ES256 signing key; generating one at startup invalidates tokens.
- Keep
resource,public_origin, protocol routes, and Weldall registration exact. - Use HTTPS.
allow_insecure_loopback=Trueis only for local loopback development. - Use a shared atomic replay store when horizontally scaled.
in_memory()is process-local;replay_store="disabled"explicitly accepts replay risk and cannot be used with skill publication. - Call
ready()at startup when discovery and JWKS availability must fail early. - Plan key rotation, short token lifetimes, and the current lack of DPoP nonce negotiation.
Development and packaging
uv sync --locked --all-extras
uv run ruff format --check .
uv run ruff check .
uv run mypy src tests/typing
uv run pytest
uv build
The wheel and sdist include LICENSE and py.typed. Local development never
publishes a package. The manual release workflow accepts only an explicitly
authorized weldall-sdk-vX.Y.Z tag and runs through a protected environment.
Publish to TestPyPI first. Authorize PyPI in a separate run after checking the
result.
License
FSL-1.1-ALv2 (Functional Source License), converting to Apache-2.0 two years after each release. PyPI may display this nonstandard SPDX identifier as “Other”; the complete byte-identical license text is included in every distribution.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file weldall_sdk-0.1.0.tar.gz.
File metadata
- Download URL: weldall_sdk-0.1.0.tar.gz
- Upload date:
- Size: 127.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c6262df35f4519a11d1d409f13339674c2470c73aff819ce8fc3df43bfc33730
|
|
| MD5 |
c47308a1579519961c93d993a260a3b7
|
|
| BLAKE2b-256 |
656e4208fb65dc319b8396d03724f66a39e18026ca6734dcc2bdc0e0131d8f28
|
Provenance
The following attestation bundles were made for weldall_sdk-0.1.0.tar.gz:
Publisher:
release-python-sdk.yml on seibert-external/weldall
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
weldall_sdk-0.1.0.tar.gz -
Subject digest:
c6262df35f4519a11d1d409f13339674c2470c73aff819ce8fc3df43bfc33730 - Sigstore transparency entry: 2721609832
- Sigstore integration time:
-
Permalink:
seibert-external/weldall@df02031e439fc087f3b54c03379eddb7f6327b77 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/seibert-external
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-python-sdk.yml@df02031e439fc087f3b54c03379eddb7f6327b77 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file weldall_sdk-0.1.0-py3-none-any.whl.
File metadata
- Download URL: weldall_sdk-0.1.0-py3-none-any.whl
- Upload date:
- Size: 46.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
70f9a17e9d199276956b24e9fc924fc707f37ce6d927b8113c3723b689eb5369
|
|
| MD5 |
495c0df33651b104d14c147288b7bafb
|
|
| BLAKE2b-256 |
bcc8e747570f9bc02ab2c01db972ecf5eaef835ccb315e26d76671f5c0097cfc
|
Provenance
The following attestation bundles were made for weldall_sdk-0.1.0-py3-none-any.whl:
Publisher:
release-python-sdk.yml on seibert-external/weldall
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
weldall_sdk-0.1.0-py3-none-any.whl -
Subject digest:
70f9a17e9d199276956b24e9fc924fc707f37ce6d927b8113c3723b689eb5369 - Sigstore transparency entry: 2721609860
- Sigstore integration time:
-
Permalink:
seibert-external/weldall@df02031e439fc087f3b54c03379eddb7f6327b77 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/seibert-external
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-python-sdk.yml@df02031e439fc087f3b54c03379eddb7f6327b77 -
Trigger Event:
workflow_dispatch
-
Statement type: