Overview
Qascade exposes one primitive — the State Update Qascade Agent (SUQA) — in two equivalent
shapes: a stable HTTP API mounted at /v1/, and a Python SDK in suqa.services
that the demos and the HTTP views both call. Anything you can do over HTTP you can do in-process.
Base URL: https://<host>/v1 (use http://localhost:8000/v1 for local dev) · Content-Type: application/json · IDs are prefixed: state_…, tr_…, red_…
Auth & service identity
who's calling, and can they?Every mutating HTTP call must carry a service identity header. Unknown services receive 401 before the service layer is ever reached.
X-Service-Identity: risk-service
Identities are validated against an allowlist in suqa/identity.py. The same header populates created_by, requested_executor, and redeemer on audit events.
authorize_transform that bind sub · op · executor · purpose · exp. A TAT is the only way to submit a transformed state.Data model
four objects, one lifecycle| Object | Purpose | Key fields |
|---|---|---|
| StateEnvelope | An executable state. Single-use. | state_id · state_type · content_hash · status (active/consumed/expired) · parent_state_id |
| StatePolicy | What may be done with a state. | allowed_transformations · delegated_executors · ttl_days |
| TransformationRequest | Server-side record of an authorized transform. | transformation_request_id · parent_state · operation · requested_executor |
| RedemptionRecord | One attempt to redeem. Exactly one succeeds per (state, idempotency_key). |
redemption_id · state · idempotency_key · outcome (consumed/blocked) · redeemer |
StateEnvelope enforces IMMUTABLE_FIELDS at save() time (payload, hash, ids, lineage). Every transform mints a new child — you never overwrite history.Errors
machine-readable codes| code | HTTP | meaning |
|---|---|---|
| unauthorized | 401 | Missing/unknown X-Service-Identity. |
| not_found | 404 | State / transformation request doesn't exist. |
| state_not_active | 409 | State is consumed or expired. |
| state_consumed | 409 | Second redemption attempt — one already won. |
| state_expired | 409 | TTL elapsed. Transitioned lazily on access. |
| policy_violation | 403 | Operation not in allowed_transformations, or executor not delegated. |
| tat_invalid | 401 | TAT signature bad, expired, or op/executor/sub mismatch. |
| validation_error | 400 | Missing/malformed field, or immutable-field mutation attempt. |
| field | type | req | meaning |
|---|---|---|---|
| state_type | string | yes | Domain type, e.g. fintech.loan.commit.v1. |
| encrypted_payload | string | yes | Opaque blob. Content is hashed; hash is immutable. |
| policy | object | yes | allowed_transformations, delegated_executors, ttl_days. |
| metadata | object | no | Free-form routing info. |
POST /v1/states
X-Service-Identity: risk-service
Content-Type: application/json
{
"state_type": "fintech.loan.commit.v1",
"encrypted_payload": "enc::{amount:25000}",
"policy": {
"allowed_transformations": ["underwrite","price"],
"delegated_executors": ["underwriting-service"],
"ttl_days": 7
},
"metadata": {"loan_id":"L-42"}
}{
"state_id": "state_7f1c2e8b49a9",
"status": "active",
"content_hash": "sha256:4e9b…",
"parent_state_id": null,
"created_at": "2025-04-20T22:12:04Z"
}Returns the envelope. If ttl_days has elapsed, the state is durably transitioned active → expired as a pre-flight UPDATE before the response is built.
GET /v1/states/state_7f1c2e8b49a9 X-Service-Identity: underwriting-service
{
"state_id": "state_7f1c2e8b49a9",
"state_type": "fintech.loan.commit.v1",
"status": "active",
"content_hash": "sha256:4e9b…",
"parent_state_id": null,
"policy": {"allowed_transformations":["underwrite","price"],"ttl_days":7}
}Returns the chain from this state up to the originating root, in order. Useful for audit ("what decision led to this execution?") and for refining policies along the chain.
{
"lineage": [
{"state_id":"state_9aa2…","state_type":"fintech.price.v1","parent_state_id":"state_7f1c…"},
{"state_id":"state_7f1c…","state_type":"fintech.loan.commit.v1","parent_state_id":null}
]
}| field | type | req | meaning |
|---|---|---|---|
| operation | string | yes | Must appear in parent's allowed_transformations. |
| requested_executor | string | yes | Must appear in parent's delegated_executors. |
| purpose | string | no | Free-form audit reason, bound into the TAT. |
| ttl_seconds | int | no | TAT lifetime. Default short — minutes, not days. |
POST /v1/states/state_7f1c…/transform
X-Service-Identity: risk-service
{"operation":"underwrite",
"requested_executor":"underwriting-service",
"purpose":"credit-check",
"ttl_seconds": 300}{
"transformation_request_id": "tr_d3c9…",
"tat": "eyJhbGciOi…<signed>…",
"expires_at": "2025-04-20T22:17:04Z"
}403 policy_violation) are returned here, not later — a rogue caller can't mint a TAT for an operation the parent never permitted.The TAT's sub/op/executor claims must match the server-side TransformationRequest. On success, a new child StateEnvelope is created with parent_state_id set and the parent's policy inherited.
POST /v1/states/submit
X-Service-Identity: underwriting-service
{
"tat": "eyJhbGciOi…",
"state_type": "fintech.underwrite.v1",
"encrypted_payload": "enc::{score:720}",
"metadata": {"model":"uw-v3"}
}{
"state_id": "state_9aa2…",
"parent_state_id": "state_7f1c…",
"status": "active"
}Creates a sibling state with identical content but a new state_id. The original remains active. Useful when multiple consumers need to reason about the same decision without racing for its one redemption.
| field | type | req | meaning |
|---|---|---|---|
| idempotency_key | string | yes | Client-chosen key. Unique together with state_id. |
| reason | string | no | Free-form audit note. |
Guarded server-side by select_for_update and a UniqueConstraint(state, idempotency_key). Under N concurrent callers, exactly one gets consumed; the others get blocked. Retrying with the same idempotency key is safe.
{
"redemption_id": "red_5b12…",
"state_id": "state_7f1c…",
"outcome": "consumed",
"redeemer": "execution-service"
}{
"error": "state_consumed",
"redemption_id": "red_a4ee…",
"outcome": "blocked"
}state_id: at most one consumed redemption ever exists. Proven by a threaded race test in the suite.{
"redemptions": [
{"redemption_id":"red_5b12…","outcome":"consumed","redeemer":"agent-b","at":"…"},
{"redemption_id":"red_a4ee…","outcome":"blocked","redeemer":"agent-a","at":"…"},
{"redemption_id":"red_91c0…","outcome":"blocked","redeemer":"agent-c","at":"…"}
]
}This is what makes "attempted many times, realized exactly once" provable after the fact. It's what Act 6 of the story demo renders as the final ledger.
suqa.services.create_state(...)
SDKmutatessame code path as POST /v1/statesfrom suqa import services state = services.create_state( state_type="fintech.loan.commit.v1", encrypted_payload="enc::{amount:25000}", created_by="risk-service", policy={ "allowed_transformations": ["underwrite", "price"], "delegated_executors": ["underwriting-service"], "ttl_days": 7, }, metadata={"loan_id": "L-42"}, ) # -> StateEnvelope(state_id='state_7f1c…', status='active')
Returns StateEnvelope. Raises ValidationError on malformed policy/payload. Emits audit event state_created.
suqa.services.duplicate_state(state_id, actor)
SDKmutatesfork content, new identity, parent unchangedsibling = services.duplicate_state(state.state_id, actor="risk-service") # sibling.state_id != state.state_id, but content_hash matches.
Emits state_duplicated. Does not consume the original; both remain active until one is redeemed.
suqa.services.authorize_transform(...)
SDKmutatesmint a scoped, signed TATreq, tat = services.authorize_transform( state_id=state.state_id, operation="underwrite", requested_executor="underwriting-service", actor="risk-service", purpose="credit-check", ttl_seconds=300, ) # req: TransformationRequest (persisted, with tr_ id) # tat: signed capability string — hand this to the executor
Raises PolicyViolation if operation isn't allowed or the executor isn't delegated. Emits transform_authorized.
suqa.services.submit_transformed_state(...)
SDKmutatesverify TAT, mint child, inherit policychild = services.submit_transformed_state( tat=tat, state_type="fintech.underwrite.v1", encrypted_payload="enc::{score:720}", actor="underwriting-service", metadata={"model": "uw-v3"}, ) # child.parent_state_id == state.state_id # child.policy inherited from parent — immediately usable
Raises TATInvalid if signature/expiry is bad or the TAT's sub/op/executor don't match the persisted TransformationRequest. Emits transformed_state_submitted.
suqa.services.get_lineage(state_id)
SDKreadlist ancestors, child → rootfor node in services.get_lineage(child.state_id): print(node.state_id, node.state_type)
suqa.services.redeem_state(...)
SDKmutates · irreversiblethe one that flips the bitresult = services.redeem_state( state_id=child.state_id, idempotency_key="exec-L-42-attempt-1", redeemer="execution-service", reason="loan commit", ) if result.outcome == "consumed": do_the_irreversible_thing() else: # 'blocked' — someone else already won, or this is a retry log.info("skipped", original_winner=result.existing_redeemer)
state_id returns outcome='consumed'. Retrying with the same idempotency_key is safe and replays the original outcome. Emits state_redeemed or state_redemption_blocked.suqa.tokens
SDKhelpersTAT mint/verify (low-level)Most code should never touch this module directly — authorize_transform and submit_transformed_state call it for you. It's exposed for testing and for integrations that need to validate a TAT at the edge.
from suqa import tokens tat = tokens.mint_tat( sub=req.transformation_request_id, op="underwrite", executor="underwriting-service", purpose="credit-check", ttl_seconds=300, ) claims = tokens.verify_tat(tat) # {'jti','sub','op','executor','purpose','iat','exp'}
Signed with django.core.signing. Tampering, expiry, or mismatched sub/op/executor all raise TATInvalid.
suqa/views.py are thin adapters over the SDK in suqa/services.py. The CLI demos and the web demos on this site call the SDK directly — which is why the invariants hold identically in all three.