From ca8a98acbd177a23dcc73b17ede579b145230ed9 Mon Sep 17 00:00:00 2001 From: Sharath Rajasekar Date: Mon, 31 Aug 2026 21:11:41 -0700 Subject: [PATCH 01/14] docs: refresh ODIS statement and notebooks against the PR #304 state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Statement: graded ref bumped to the #304 head; L1-09 Holder-of-Key flips to Meets (via configuration) — token.require_dpop closes the Bearer fallback with tests cited — taking L1 to 9 Meets / 0 Partial / 3 Gap; the 6.2 holder_key_ref binding row upgraded under the require_dpop posture; SDK proof-construction gap cross-referenced (sdk#105). Notebooks: both re-executed against the hardened image (attestation clamp, CIBA anchoring with pre-burn gates and policy ceiling) on a fresh deployment — all outputs real (13 ms cascade this run). The raw walkthrough's section 5 now documents the require_dpop deployment posture; the SDK companion's gap list marks missing DPoP proof support as load-bearing under a require_dpop server. Builder scripts are the single source of truth again (earlier JSON-only edits folded back in). README gains the stale-volume upgrade note the refresh itself tripped over: default policies don't self-heal, so pre-CIBA volumes refuse section 7. Co-Authored-By: Claude Fable 5 --- docs/odis/role-capability-statement.md | 317 +++++++++ examples/odis/README.md | 64 ++ examples/odis/odis-walkthrough-sdk.ipynb | 401 ++++++++++++ examples/odis/odis-walkthrough.ipynb | 800 +++++++++++++++++++++++ 4 files changed, 1582 insertions(+) create mode 100644 docs/odis/role-capability-statement.md create mode 100644 examples/odis/README.md create mode 100644 examples/odis/odis-walkthrough-sdk.ipynb create mode 100644 examples/odis/odis-walkthrough.ipynb diff --git a/docs/odis/role-capability-statement.md b/docs/odis/role-capability-statement.md new file mode 100644 index 00000000..9cb652ec --- /dev/null +++ b/docs/odis/role-capability-statement.md @@ -0,0 +1,317 @@ +# ZeroID — ODIS Role-Capability Statement (Layers 1–2, Native Mode) + +**Status**: Draft for submission to the CoSAI/OASIS WS4 ODIS workstream +**ODIS reference**: [ODIS.md draft](https://github.com/cosai-oasis/ws4-odis/blob/main/RFCs/ODIS.md) (unapproved contributor draft) +**ZeroID version**: commit `1676b4f` on `main` (PR #304 merged — `token.require_dpop`, attestation trust-clamp, CIBA credential anchoring; Apache-2.0, ) +**Claim type**: role-capability statement per ODIS §8. **This is not an ODIS Core, Extended, or Safety profile claim** — §8 reserves those for a complete conformance target, and this document itself identifies unmet MUSTs. + +ZeroID is an OAuth 2.1/OIDC authorization server for AI-agent identity, built +independently of ODIS and mapped to it after the fact. It implements the +substance of ODIS **Layer 1 (Passport)** and **Layer 2 (Bridge, native mode)**: +registration records, attestation-gated short-lived proof-of-possession +credentials, delegation records with monotonic attenuation, asynchronous human +authorization, cascade revocation, and dual-identity audit. It does not +implement Layer 3 governance (checkpoint/policy-engine/velocity), bridge-mode +provider adapters, or software/hardware attestation; those are marked **Gap** +below, not argued around. + +## How to read this + +- **Meets** — the requirement's normative substance is implemented and tested. +- **Meets (via configuration)** — a supported configuration of shipped code + fully satisfies the MUST; the note names the exact configuration. ODIS §8 + grades a *configured conformance target*, not a source tree, so this is a + full Meets for any deployment that adopts the named posture. +- **Partial** — implemented with a stated limitation that matters. +- **Gap** — not implemented in the open-source core. Where ZeroID exposes a + deliberate **extension point** for the capability (verifier registry, + notifier hooks, resolver interfaces), the note names it — a deployer or a + commercial distribution can build there, but per §8 an unbuilt extension + carries no conformance evidence, so the verdict stays Gap until code exists. +- **N/A** — the requirement governs a component ZeroID does not contain (stated, not skipped). +- **Evidence** — repo-relative paths and greppable test names. Every pointer in + this document was verified against the commit above; run + `go test ./tests/integration/ -run ` to reproduce any of them + (see `tests/integration/COMPLIANCE.md` for the per-RFC matrix). + +Companion: [`examples/odis/`](../../examples/odis/) — an executable notebook +that walks ODIS terminology (Agent Registration Record → Agent Runtime +Credential → Delegation Record → revocation → audit) against a locally running +ZeroID, with real outputs committed. + +## Summary + +| Layer | Meets | Partial | Gap | N/A | +|---|---|---|---|---| +| L1 — Identity & Attestation (12 reqs) | 9 | 0 | 3 | 0 | +| L2 — Delegation & Access (15 reqs) | 6 | 4 | 3 | 2 | +| L3 — Discovery & Governance (declared out of role; 8 reqs) | 1 | 3 | 4 | 0 | +| CC — Cross-cutting (7 reqs) | 4 | 0 | 2 | 1 | + +The Gap column is the point of publishing: it is implementation feedback on +which ODIS requirements are the real adoption cliff for an OAuth-native +authorization server (software attestation, bridge mode, presenter isolation, +published benchmarks). + +## The map, in ODIS's own figure + +The diagram mirrors the ODIS §3 three-layer architecture figure (same shape, +same layer palette) with ZeroID's components in the boxes. Solid boxes are +implemented, with evidence in the tables below; dashed boxes are the declared +gaps or out-of-role layers. + +```mermaid +flowchart TB + +Agent["Agent runtime\nholds its own holder key"] + +subgraph L1["ODIS Layer 1 — Passport"] +direction LR +WL["Workload attestation\ngeneric OIDC verifier + per-tenant policy\nGitHub Actions · GCP WIF · K8s"] +SW["Software attestation\nL1-02 / L1-08 — GAP"] +HW["Hardware attestation\nL1-04 — GAP"] +TRUST["Trust promotion\nunverified → verified_third_party → first_party\nexactly-once, in-transaction"] +ARC["Agent Runtime Credential\nshort-TTL JWT · DPoP cnf.jkt\natomic jti replay ledger"] +WL --> TRUST --> ARC +SW -.-> ARC +HW -.-> ARC +end + +ARR["Agent Registration Record\nidentity + CredentialPolicy\nWIMSE URI · lifecycle states · accountable owner"] + +subgraph L2["ODIS Layer 2 — Bridge, native mode"] +direction LR +CIBA["Bounded async authorization\nCIBA poll/ping/push + RAR"] +TE["RFC 8693 token exchange\nthree-way scope intersection · act chain\nmission_id · depth cap · expiry clamp"] +DR["Delegation claims\n§6.3 carrier: 6 of 13 fields\nsee field mapping"] +PA["Provider Adapter / bridge mode\nL2-08..10 — GAP\ncompanion broker in development"] +CIBA --> TE --> DR +end + +TARGET["Native-mode target\npkg/authjwt Go · SDK tokens.verify Python\nscope / trust / delegation guards"] +REV["Revocation plane\nCAE signal ingest → cascade via parent_jti\nSSE stream · offboard kill switch"] +L3["ODIS Layer 3 — Router\noutside this statement's role\ngovernance checkpoint · policy engine · velocity"] + +Agent --> L1 +ARC --> L2 +ARR -.->|"L2-14: resolve active registration\nIsUsable at the issuance chokepoint"| L2 +DR --> TARGET +REV -.->|"revocation semantics\nintrospection / event stream"| TARGET +L2 -.-> L3 + +style L1 fill:#8b6914,color:#fff,stroke:#6b5010 +style L2 fill:#4a7c59,color:#fff,stroke:#3a6040 +style Agent fill:#333,color:#fff,stroke:#555 +style WL fill:#a07820,color:#fff,stroke:#806018 +style TRUST fill:#a07820,color:#fff,stroke:#806018 +style ARC fill:#a07820,color:#fff,stroke:#806018 +style CIBA fill:#5a9060,color:#fff,stroke:#4a7050 +style TE fill:#5a9060,color:#fff,stroke:#4a7050 +style DR fill:#5a9060,color:#fff,stroke:#4a7050 +style SW fill:#f5f5f5,color:#333,stroke:#aaa,stroke-dasharray: 5 5 +style HW fill:#f5f5f5,color:#333,stroke:#aaa,stroke-dasharray: 5 5 +style PA fill:#f5f5f5,color:#333,stroke:#aaa,stroke-dasharray: 5 5 +style L3 fill:#f5f5f5,color:#333,stroke:#aaa,stroke-dasharray: 5 5 +style ARR fill:#f0f0f0,color:#333,stroke:#999 +style TARGET fill:#f0f0f0,color:#333,stroke:#999 +style REV fill:#f0f0f0,color:#333,stroke:#999 +``` + +## Layer 1 — The Passport (Identity & Attestation) + +| Req | Verdict | Evidence | Notes | +|---|---|---|---| +| **ODIS-L1-01** Secret-Zero Elimination | **Meets (via configuration)** | Private-key flows: RFC 7523 jwt-bearer (`tests/integration/jwt_bearer_compliance_test.go`), DPoP (`pkg/dpop/`). Static path: `internal/service/oauth.go` `apiKeyGrant`, `domain/credential.go` `GrantTypeAPIKey` | The conformant posture is one policy line: a `CredentialPolicy` whose `AllowedGrantTypes` excludes `api_key` leaves only private-key flows (jwt-bearer, token exchange) on the agent path — fully satisfying the MUST for that deployment. The static `api_key` grant remains in `domain.DefaultAllowedGrantTypes()` as a bootstrap convenience; out-of-the-box defaults are not the conformance target. | +| **ODIS-L1-02** Software Attestation | **Gap** | `domain/attestation.go` declares `image_hash`; only `internal/attestation/stub.go` `DevStubVerifier` backs it | No digest/signature/provenance verification ships in the core (no sigstore/in-toto/SLSA integration). The deliberate extension point exists: the `internal/attestation` `Verifier` registry dispatches by proof type, and `image_hash` is reserved for exactly this — a deployer or distribution binds a real provenance verifier there without core changes. | +| **ODIS-L1-03** Runtime/Workload Attestation | **Meets** | `internal/attestation/oidc.go` (generic `OIDCVerifier`: JWKS discovery, issuer allow-list, audience, `required_claims`, SSRF-guarded transport); per-tenant `AttestationPolicy` (`internal/attestation/policy.go`); fail-closed: `TestAttestationFailsClosedWithNoPolicy`, `TestAttestationOIDCVerifierRejectsUntrustedIssuer`, `TestAttestationDoubleVerifyIsRejected` | One generic OIDC verifier; GitHub Actions, GCP Workload Identity Federation, and Kubernetes projected SA tokens are worked *configurations* (`docs/attestation.md`), not per-provider code. | +| **ODIS-L1-04** Hardware Attestation | **Gap** | `tpm` proof type exists; stub-only | Same status and same extension point as L1-02: bind a TPM/TEE verifier into the registry. | +| **ODIS-L1-05** Credential Lifecycle | **Meets** | `domain/credential_policy.go` (`MaxTTLSeconds`, default 3600), enforcement `internal/service/credential_policy.go` `EnforcePolicy`; child exp clamped to parent (`TestTokenExchangeChildClampedToParentExpiry`); refresh rotation with reuse-detection family revocation (`tests/integration/refresh_token_race_test.go`) | Lifetimes are configurable, finite, policy-bounded. Automatic rotation before expiry is enforced by the SDK component of the declared target: the token manager refreshes ahead of expiry via a built-in buffer (`highflame/zeroid/_token_manager.py`, `_TOKEN_REFRESH_BUFFER`), with the server re-gating identity state and current policy on every rotation. | +| **ODIS-L1-06** Provisioning & De-provisioning | **Meets** | Lifecycle states `discovered→pending→active→suspended→deactivated/expired` (`domain/identity.go` `CanTransitionTo`); adopt/dismiss (`internal/handler/identity.go`); offboard-by-owner cascade (`internal/service/identity.go` `OffboardOwner`, DB `revoke_credentials_by_owner_cascade`); `tests/integration/discovery_lifecycle_test.go` | Satisfied via the "equivalent lifecycle-management mechanisms" clause — SCIM protocol endpoints themselves are not implemented. | +| **ODIS-L1-07** Federated Trust *(Extended)* | **Meets** | Direct external-issuer OIDC federation: `external_issuers` config (`domain/external_issuer.go`), verification `internal/service/oauth_external_idp.go` (`subject_token_type=…:id_token`), upstream `iss` propagated as `user_id_iss`; `TestExternalIDTokenFederation_EndToEnd`, `_KeyRotation`, `_CrossTenantRejected` | Deployer-configured trust anchors; no automatic federation discovery. A trusted-service broker path also exists (`ExternalPrincipalExchange`) and is documented as the lossier fallback. | +| **ODIS-L1-08** Trusted Distribution | **Gap** | — | No supply-chain verification before identity issuance. Pairs with L1-02. | +| **ODIS-L1-09** Holder-of-Key Authentication | **Meets (via configuration)** | DPoP with atomic jti replay ledger (`pkg/dpop/verifier.go`, `TestDPoPReplayRejected`); refresh tokens key-bound (`TestDPoPRefreshBoundWithDifferentKeyRejected`); binding propagates through delegation (`TestDPoPTokenExchange_PropagatesBindingToSubAgent`); WIMSE single-use nonce-bound proof tokens (`internal/service/proof.go`); **enforcement switch**: `token.require_dpop` refuses proof-less issuance with `invalid_dpop_proof` on every grant and advertises `dpop_bound_access_tokens_required` per RFC 9449 §5.1 (`TestRequireDPoPRefusesProoflessIssuance`, `TestRequireDPoPAdvertisedInASMetadata`) | The conformant posture is `token.require_dpop: true` (default off preserves Bearer opt-in): every issued credential is then proof-of-possession-bound. Client-side note: the Python SDK (0.3.17) does not yet construct DPoP proofs (tracked upstream, sdk#105), so a require_dpop deployment currently serves raw-HTTP/self-signing clients — the notebook demonstrates proof construction in a few lines of PyJWT. | +| **ODIS-L1-10** Accountable Sponsor | **Meets** | `owner_user_id` verified against the tenant directory (CAP-DSC-004, `internal/service/identity.go`); offboard-by-owner cascade; ownerless as a surfaced posture signal | ODIS's administrative *drain* state is not implemented — lifecycle events revoke rather than drain. | +| **ODIS-L1-11** Attestation-Bootstrapped Trust | **Meets (via configuration)** | Attestation raises `trust_level` exactly-once in-transaction (`internal/service/attestation.go` `trustLevelForAttestation`); `CredentialPolicy.RequiredTrustLevel`/`RequiredAttestation` gate issuance (`EnforcePolicy` checks 4–5); expiry demotes (`TestExpiredAttestationNoLongerSatisfiesPolicy`) | The conformant posture: `RequiredTrustLevel`/`RequiredAttestation` on the identity's `CredentialPolicy` makes issuance depend on verified attestation, fail-closed (demonstrated live in the companion notebook, §3–§4). Tenants that omit the requirement have chosen a non-ODIS posture; the enforcement machinery is shipped and tested. | +| **ODIS-L1-12** Runtime Security State | **Meets** | Signal ingest `POST /signals/ingest` (`internal/handler/signal.go`); severity-driven revocation with cascade (`TestCAECriticalSignalRevokesCredential`, `TestCAESignalCascadesRevocationToChildren`) | Signal schema is ZeroID-local, not CAEP event-type URIs (see `COMPLIANCE.md`'s honest SSF/CAEP row). | + +## Layer 2 — The Bridge (Delegation & Access, native mode) + +| Req | Verdict | Evidence | Notes | +|---|---|---|---| +| **ODIS-L2-01** Delegated Authorization | **Partial** | Three-way scope intersection at exchange (`internal/service/oauth.go` `tokenExchange`: requested ∩ subject-granted ∩ actor-policy); empty intersection fails closed (`invalid_scope`) | Partial under a strict reading: the MUST enumerates the intersection's inputs as principal ∩ registration ∩ parent ∩ **task ∩ resource ∩ environmental constraints** ∩ adapter mapping. ZeroID intersects scopes/depth/TTL, resolves the registration, clamps to the parent, and re-verifies the principal on refresh — but models no task/resource/constraint dimensions. Fail-closed behavior itself is complete. | +| **ODIS-L2-02** Bounded Authorization | **Meets** | CIBA poll/ping/push (`internal/service/backchannel.go`, `TestCIBACore1_0_S11_AuthorizationPendingWhilePending`); RAR typed `authorization_details` bound into the approval and the issued JWT (`TestRFC9396_S6_1_AccessTokenJWTEmbedsAuthorizationDetails`, `ciba_rar_test.go`) | Async human approval bound to the request's declared authority — ODIS's headless-agent clause implemented with standard protocols. | +| **ODIS-L2-03** Session Continuity | **Meets** | Refresh re-gates identity usability before *and* after rotation (`internal/service/oauth.go` `refreshTokenGrant`); DPoP binding re-checked; reuse detection revokes the family (`refresh_token_race_test.go`) | Fail-closed on revocation, deactivation, expiry, or binding mismatch. | +| **ODIS-L2-04** Durable Delegation *(Extended)* | **Meets** | Refresh tokens with rotation + reuse-detection (`refresh_token_race_test.go`); every refresh re-gates identity state and re-runs current policy | This is the SHOULD's substance: a pre-authorized window in which authority auto-renews without new human interaction, bounded by lifecycle state, policy, and revocation — the window construct is the refresh-token family. | +| **ODIS-L2-05** Delegation Record | **Partial** | `act` chain (`domain/token.go` `ActorClaims`, `TestRFC8693_S4_2_ActClaimChainsDelegation`), `parent_jti`, `mission_id` lineage (`TestMissionID_ChainPropagation`), `delegation_depth`, child expiry clamped to parent | Carried as JWT claims (§6.3 permits any integrity-protected carrier; integrity = the AS signature), but several §6.3 MUST fields have no equivalent — see the field mapping below the table. | +| **ODIS-L2-06** Authorization Attenuation | **Partial** | Monotonic narrowing via set intersection over a controlled scope vocabulary; depth cap; `TestRFC8693_*` + `subagent_delegation_test.go` | This is lexical-with-controlled-vocabulary, not the semantic `attenuation_profile_ref` mechanism ODIS specifies. Honest reading: sufficient when the issuer owns the vocabulary, insufficient for cross-vendor scope semantics. | +| **ODIS-L2-07** Contextual Re-verification | **Meets** | Pre-rotation identity gate + full `EnforcePolicy` re-run against *current* policy on every refresh (`refreshTokenGrant`; `deactivation_test.go`) | | +| **ODIS-L2-08** Backward Compatibility (bridge) | **Gap** | — | ZeroID is native-mode only: downstream services validate ZeroID JWTs (`pkg/authjwt`). Target-native credential translation is a companion credential-broker component under development, out of this statement's scope. | +| **ODIS-L2-09** Bridge Mapping | **Gap** | — | With L2-08. | +| **ODIS-L2-10** Fail-Closed Attenuation (bridge) | **Gap** | — | With L2-08. | +| **ODIS-L2-11** Revocation-Safe Credential Reuse | **N/A** | Within its own token plane: cascade revocation + `RevocationNotifier` fire per-JTI (`TestRevocationNotifier_FiresOncePerJTIOnCascade`) | ZeroID does not cache derived downstream credentials, so the reuse-TTL bound has no object here; it binds the (future) broker component. | +| **ODIS-L2-12** Presenter Continuity | **Meets** | Same-key enforcement across the credential's whole life: refresh rotation rejects a different holder key (`TestDPoPRefreshBoundWithDifferentKeyRejected`), refresh without proof rejected (`TestDPoPRefreshBoundWithoutProofRejected`), and the binding propagates through delegation (`TestDPoPTokenExchange_PropagatesBindingToSubAgent`) | The requirement is conditional ("*when* Layer 2 issues a holder-of-key-bound credential") and ZeroID enforces exactly its substance for every bound credential: no key substitution, no export-and-rebind without re-issuance. Key custody location is L2-13/Pattern-4 territory, not this row. | +| **ODIS-L2-13** Presenter Authority Scoping | **Partial** | The WIMSE proof service is the non-generic presenter primitive the MUST describes: fixed claim shape (never arbitrary payloads), audience-bound, single-use via DB-unique nonce, and identity-state-gated before construction (`internal/service/proof.go` — `IsUsable`/expiry checked before signing; `TestProofTokenSingleUseUnderConcurrency`) | What's missing: action-level validation against the delegation record's authority, and routing DPoP proof construction through a scoped presenter — today DPoP proofs are client-constructed with directly-held keys (Pattern 4). This is exactly the surface ODIS's pending CT-P4 suite exists for — feedback for the WG: SDK-first implementations need it defined. | +| **ODIS-L2-14** Agent Registration Resolution | **Meets** | `Identity.IsUsable()` gates 15 call sites including the issuance chokepoint (`internal/service/credential.go`); `discovered` rows are inert inventory by construction (`domain/identity.go`, `discovery_lifecycle_test.go`) | ODIS's "resolve to an *active* registration before authority" is structural here, not a check bolted on. | +| **ODIS-L2-15** Provider Adapter Egress Mode | **N/A** | — | No adapters; native mode implicit. | + +### §6.3 Delegation Record — field-by-field against ZeroID's claims + +| §6.3 MUST field | ZeroID equivalent | Status | +|---|---|---| +| `delegation_id` | `jti` | present | +| `issuer` | `iss` (the AS) | present | +| `parent_delegation_ref` | `parent_jti` — but no `record_digest`; the parent is resolved by ID, digest-matching is not performed | partial | +| `originating_principal` | `act.sub` / `owner_user_id` when a human roots the chain | partial (machine-rooted chains carry no distinct originating principal) | +| `originating_authorization_ref` | — | absent | +| `actor` | `sub` (WIMSE URI) | present | +| `delegation_chain` | single-level `act` + reconstruction via `/delegations/by-jti` — the record itself does not carry the ordered hop list | partial | +| `task_id` | `mission_id` — a chain **correlation** key, not a declared purpose | partial (see zeroid#222: link mission_id to human-authored intent) | +| `granted_authorizations` | `scope` after intersection | present | +| `resource_indicators` | — (RFC 8707 `resource` unsupported; open zeroid#258) | absent | +| `constraints` | — | absent | +| `attenuation_profile_ref` | — (the L2-06 gap) | absent | +| `issued_at` / `expires_at` | `iat` / `exp`, child clamped to parent | present | + +This mapping is offered to the workstream as implementation feedback: an +OAuth-native carrier gets 6 of 13 fields for free, 3 partially, and the 4 +absent ones (`originating_authorization_ref`, `resource_indicators`, +`constraints`, `attenuation_profile_ref`) are exactly the fields with no +established OAuth claim to inherit — candidates for a minimal JWT claim +profile the spec could publish. + +## Layer 3 — The Router (outside this statement's role, stated anyway) + +| Req | Verdict | Evidence / note | +|---|---|---| +| ODIS-L3-01 Tool/Service Discovery | **Partial** — observed-resource registry learned from ID-JAG redemptions, deliberately evidence-not-assertion (`internal/handler/observed_resources.go`) | +| ODIS-L3-02 Governance Checkpoint | **Gap** — belongs to a gateway/router component, not the AS | +| ODIS-L3-03 Velocity Limits | **Gap** — a dormant `rate_limit_rps` column exists and is read nowhere; CIBA `slow_down` is protocol pacing, not rate limiting | +| ODIS-L3-04 Revocation Latency | **Partial** — the *mechanism* is synchronous cascade in-transaction + SSE push (`GET /signals/stream`) + `RevocationNotifier` hooks; a declared, measured maximum latency (what ODIS actually requires) is not published — see CC-03 | +| ODIS-L3-05 Kill Switch | **Meets** — identity deactivation and offboard-by-owner cascade-revoke all credentials in one operation (`revoke_credentials_by_owner_cascade`, `TestCAESignalRevokesAllActiveCredentials`) | +| ODIS-L3-06 Policy Engine Integration | **Gap** — no OPA/Cedar callout in ZeroID's request path; ID-JAG maps IdP claims into Cedar-shaped principal attributes for *downstream* engines (`oauth_id_jag.go`), which is claim shaping, not checkpoint integration. The §6.4 object itself is assemblable from existing APIs — see the inter-layer payload appendix | +| ODIS-L3-07 Task-Bound Tokens *(Extended)* | **Partial** — `mission_id` (delegation-tree purpose anchor) and RAR `authorization_details` carry declared intent in the token; no checkpoint validates actions against it inside ZeroID | +| ODIS-L3-08 Boundary Protection *(Extended)* | **Gap** — with L3-02 | + +## Cross-cutting + +| Req | Verdict | Evidence | Notes | +|---|---|---|---| +| **ODIS-CC-01** Observability | **Meets** | Identity audit tables + trigger (`migrations/010`, `012`); delegation explorer `/delegations/graph`, `/by-jti/{jti}`, `/chains` (`internal/handler/delegation.go`, 33 tests incl. `TestDelegationByJTI_WalksToRoot`); credentials retained past expiry on a dual-clock sweeper (`internal/worker/cleanup.go`) so lineage survives for forensics | Audit logs are not hash-chained (a SHOULD; MUST only in the Safety profile). | +| **ODIS-CC-02** Dual-Identity Audit Trail | **Meets** | Tokens carry agent `sub` + `act.sub` (originating human — preserved even through key rotation, commit `220321f`) + `owner_user_id`; `TestRFC8693_S4_2_ActClaimChainsDelegation` | | +| **ODIS-CC-03** Latency Budget | **Gap** | No `Benchmark*` functions, no published p50/p95/p99 report anywhere in-repo | ODIS requires a *published, reproducible* benchmark report; none exists. | +| **ODIS-CC-04** Availability | **Gap** | No published availability objective/measurement | With CC-03. | +| **ODIS-CC-05** Governed Identity Creation | **Meets** | Registration via authenticated admin surface; discovered rows require human adopt before usability; agents cannot mint or expand their own registration (`AdoptIdentity`, transition matrix) | | +| **ODIS-CC-06** Terminal Exchange Audit Anchor | **N/A** | ID-JAG redemptions record observed resources with single-use jti enforcement (`internal/store/postgres/id_jag_jti.go`) | Terminal exchange into non-ODIS systems is the broker component's obligation, not the AS's. | +| **ODIS-CC-07** Data Protection | **Meets** | Query strings stripped from access logs (`logSafePath`, `log_redaction_test.go`); OAuth error-value redaction on token/introspect/revoke/bc-authorize (`internal/handler/routes.go` `redactErrorValues`); raw connector/API secrets never persisted (hashed/ referenced); configurable audit retention with deletion sweeper | | + +## Appendix — the inter-layer payload contract (§6.1 / §6.2 / §6.4) + +ODIS mandates no wire format ("JWT claims, protocol buffers, JSON-LD" are all +valid carriers) but does mandate what each layer hands the next: the Passport +emits an Agent Runtime Credential Descriptor (§6.2) that must reference the +Agent Registration Record (§6.1); the Bridge emits the Delegation Record +(§6.3 — mapped field-by-field above); and the governance checkpoint must be +fed the assembled Identity Context (§6.4), which bundles all three plus the +requested action. Each payload carries an authenticated back-reference to the +one before it. This appendix maps the remaining two. + +A note on field names: §6's schemas are **abstract** — a `credential_id` row +matched to `jti` below is not a mismatch but a *binding*, per the spec's own +carrier rule. What the spec requires is the field's semantics +("collision-resistant identifier for this Agent Runtime Credential"), and +RFC 7519's `jti` is the registered JWT claim carrying exactly those +semantics. ZeroID's bindings are documented normatively in its claim +registry (`docs/spec/zeroid-oauth-extensions.md` §14). The interoperability +risk sits one level up: ODIS publishes no canonical JWT binding, so two +conformant implementations could bind the same abstract field to different +claims — see feedback item 3. + +### §6.2 Agent Runtime Credential Descriptor — field-by-field + +| §6.2 field | ZeroID equivalent | Status | +|---|---|---| +| `credential_id` | `jti` | present | +| `format_version` | — (RFC 9068 `at+jwt` typ header is open work, zeroid#189) | absent | +| `agent_id` | `sub` (WIMSE URI) | present | +| `registration_record_ref` (issuer + id + version + digest) | registration resolved by authoritative same-database lookup at issuance *and* refresh (`IsUsable()` chokepoint) — within a single trust domain this is strictly stronger than a digest reference (always-current, no staleness window). The portable versioned ref matters only for cross-domain verification (federation), which is not this binding's deployment shape | present (co-located binding) | +| `runtime_instance_id` | — (ZeroID models the logical agent; per-issuance `jti` is the closest analogue) | absent | +| `software_hash` | — (the L1-02 gap, carried into the payload) | absent | +| `attestation_evidence[]` | `attestation_records` exist server-side and gate trust, but no evidence object is carried in or referenced by the credential | absent | +| `issuer` / `issuer_key_ref` | `iss` / JOSE `kid` against the published JWKS | present | +| `holder_key_ref` | `cnf.jkt` when DPoP-bound | present under the `token.require_dpop` posture (every credential bound); opt-in with Bearer fallback otherwise | +| `issued_at` / `expires_at` | `iat` / `exp` | present | +| `trust_domain` | the WIMSE URI's trust-domain segment | present | +| `supply_chain_ref` *(SHOULD)* | — | absent | +| `audiences` | `aud` — defaults to the issuer; RFC 8707 resource binding is open work (zeroid#199/#258) | partial | + +### §6.4 Identity Context — assembly feasibility + +ZeroID emits no assembled §6.4 object (the L3-06 Gap above). The raw material +is another story — nearly every field already exists behind an API: + +| §6.4 field | ZeroID raw material | Status | +|---|---|---| +| `agent_registration` (§6.1 object) | `GET /identities/{id}` + its `CredentialPolicy` | data exists, unassembled | +| `agent_runtime` (§6.2 descriptor) | RFC 7662 introspection (`act`, `cnf`, `trust_level`, `delegation_depth`, `authorization_details`) | data exists, §6.2 gaps carry over | +| `delegation` (§6.3 record) | `GET /delegations/by-jti/{jti}` — lineage, scopes in/out, attenuation, revocation | data exists (partial per the §6.3 mapping) | +| `action` {tool, method, resource, parameters} | supplied by the governance checkpoint at call time, not the AS | checkpoint-side by design | +| `request_timestamp` | checkpoint-assigned per §6.4 ("MUST NOT rely solely on a timestamp supplied by the agent") | checkpoint-side by design | +| `request_trace_id` | server-side `request_id` in logs only; nothing end-to-end in the token or introspection | gap | +| `runtime_risk_signals[]` | `GET /signals` (CAE signal store, typed + severities) | data exists | + +**The integration seam this exposes:** a read-only +`GET /identity-context/{jti}` that joins what ZeroID already stores would make +it the first implementation emitting §6.4 — and that object is precisely the +input the ODIS contract harness's Router/OPA checkpoint wants to be fed. One +endpoint turns "ZeroID = Layers 1–2" and "harness = Layer 3" into a running +three-layer ODIS stack. + +## Feedback to the ODIS workstream (what this mapping surfaced) + +1. **The adoption cliff is L1-02/L1-08 + L2-08..10 + L2-13.** An OAuth-native + AS can meet the identity, delegation, lifecycle, revocation, and audit + requirements of Layers 1–2 with standard protocols (RFC 7523/8693/9396/9449, + CIBA, SPIFFE-style naming). Software/supply-chain attestation, bridge-mode + translation, and presenter isolation are each a separate engineering + program. Sequencing guidance in the spec (which Core MUSTs make a useful + Phase-1) would materially help implementers. +2. **CT-P4 is load-bearing.** Every SDK-first implementation lands in + deployment Pattern 4; until the CT-P4 suite exists, none can claim Core + regardless of how much else they meet. Defining it should be a priority — + we volunteer as a test subject. +3. **Publish a canonical JWT binding for §6.2/§6.3, and a minimal + attenuation profile for L2-06.** The schemas are abstract by design, but + with no canonical claim binding, two conformant implementations can bind + the same field to different claims (`credential_id` → `jti` here; a + custom claim elsewhere) and verifiers cannot converge — a short + abstract-field → registered-JWT-claim table would fix it. Likewise + L2-06's `attenuation_profile_ref` has no example profile anywhere; our + lexical-with-controlled-vocabulary intersection is what implementers + build first, and a canonical minimal profile (exact-match over a + registered vocabulary) would let such implementations declare themselves + precisely. +4. **agent_id vs runtime instance** (their issue #6): ZeroID's split — stable + logical identity + WIMSE URI, per-issuance short-lived credentials with + per-JTI lineage — is a working answer to the uniqueness question and is + documented in `docs/spec/zeroid-oauth-extensions.md` §14 (claim registry). +5. **Platform-credential verifiability** (their issue #5): the + `AttestationPolicy` shape (per-tenant issuer allow-list + `required_claims` + + audience, one generic OIDC verifier) is a concrete data model for the + verification-method metadata that issue asks about. +6. **The §6.4 Identity Context is the natural join point between + implementations.** ZeroID holds nearly every §6.4 field behind existing + APIs (see the appendix); a single read-only assembly endpoint would emit + the first real §6.4 object — and feed it straight into the contract + harness's Router/OPA checkpoint for an end-to-end three-layer + demonstration. We propose building exactly that as the first joint + artifact. + +## Known hardening items surfaced while writing this statement + +Recorded here for honesty; tracked in the ZeroID issue tracker: + +- `attestation.allow_unsafe_dev_stub` currently defaults to **true**, wiring a + stub verifier for the `image_hash`/`tpm` proof types (the OIDC verifier is + never stubbed). The default should flip to false. +- `service_keys.rate_limit_rps` exists in schema and domain but is read + nowhere (relates to L3-03). diff --git a/examples/odis/README.md b/examples/odis/README.md new file mode 100644 index 00000000..135d8521 --- /dev/null +++ b/examples/odis/README.md @@ -0,0 +1,64 @@ +# ZeroID as ODIS Layers 1–2 + +Executable walkthroughs of ZeroID using the vocabulary of the +[ODIS draft](https://github.com/cosai-oasis/ws4-odis/blob/main/RFCs/ODIS.md) +(Open Delegation & Identity Standard, CoSAI/OASIS WS4): Agent Registration +Record → attestation-gated trust → DPoP-bound Agent Runtime Credential → +Delegation Record with monotonic attenuation → compromise-signal cascade +revocation → audit lineage. + +Two notebooks, two vantage points: + +- **[`odis-walkthrough.ipynb`](./odis-walkthrough.ipynb)** — raw HTTP, the + wire-format view: what the *authorization server* enforces at issuance + (policy-gated fail-closed issuance, DPoP holder binding and replay + rejection, attestation-driven trust promotion, the delegation explorer). +- **[`odis-walkthrough-sdk.ipynb`](./odis-walkthrough-sdk.ipynb)** — the + Python SDK (`pip install highflame`), adding the *ODIS-aware target* view + (ODIS §2.5 native mode): local JWKS verification with typed guards + (`require_scope` / `require_trust` / `is_delegated`), target-side trust + gating, attenuation's two modes (silent narrowing vs. refusal), and why + native-mode targets must check revocation state, not just signatures. + +Companion to the +[role-capability statement](../../docs/odis/role-capability-statement.md), +which maps every ODIS requirement to code and tests — including the ones +ZeroID does not meet. + +## Run it + +From the repo root: + +```bash +make setup-keys # ES256 + RSA signing keys into ./keys +docker compose up -d # zeroid + postgres on localhost:8899 +pip install requests pyjwt cryptography jupyter +jupyter notebook examples/odis/odis-walkthrough.ipynb +``` + +Upgrading an existing checkout? Tenant default credential policies are +created once and don't self-heal: a postgres volume from before the CIBA +grant joined the defaults will refuse §7's bound-client redemption +(`access_denied`). `docker compose down -v` for a fresh volume, or add the +CIBA grant to the stored default policy. + +The notebook is re-runnable: every run registers fresh identities under a +random suffix. The committed outputs are from a real run against a local +instance — if you re-execute, tokens, JTIs, and timings will differ; the +status codes and semantics will not. + +## What it demonstrates (and what it deliberately doesn't) + +Demonstrated live: fail-closed issuance for unattested identities +(ODIS-L2-14/L1-11), DPoP proof-of-possession with replay rejection +(L1-05/L1-09), three-way scope intersection and refused privilege escalation +at delegation (L2-01/05/06), critical-signal cascade revocation in +milliseconds (L1-12/L3-04/05), and delegation lineage that survives the kill +(CC-01/02). + +Not demonstrated, because ZeroID does not implement it: software/supply-chain +attestation, bridge-mode provider adapters, presenter isolation, velocity +limits. The role-capability statement documents those gaps with the same +candor. The local run also uses the dev-stub attestation proof (`image_hash`) +instead of the production OIDC workload verifier, which needs a real issuer +(GitHub Actions / GCP WIF / Kubernetes) — see `docs/attestation.md`. diff --git a/examples/odis/odis-walkthrough-sdk.ipynb b/examples/odis/odis-walkthrough-sdk.ipynb new file mode 100644 index 00000000..c2065409 --- /dev/null +++ b/examples/odis/odis-walkthrough-sdk.ipynb @@ -0,0 +1,401 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "ce5cb0d8", + "metadata": {}, + "source": [ + "# ZeroID as ODIS Layers 1–2 — the SDK companion\n", + "\n", + "Companion to [`odis-walkthrough.ipynb`](./odis-walkthrough.ipynb) (raw HTTP, wire-format\n", + "view) and the [role-capability statement](../../docs/odis/role-capability-statement.md).\n", + "This notebook replays the same ODIS story through the **Python SDK**\n", + "(`pip install highflame`), which shifts the vantage point in one important way:\n", + "\n", + "- The raw notebook shows the **authorization server** enforcing ODIS at issuance.\n", + "- This notebook also shows the **ODIS-aware target** (ODIS §2.5 \"native-mode downstream\n", + " path\", L2-15): `client.tokens.verify()` validates the Agent Runtime Credential *locally*\n", + " against the JWKS — exactly what a native-mode resource server does — and the returned\n", + " identity carries typed guards (`require_scope`, `require_trust`, `is_delegated`) that a\n", + " tool server calls before executing anything.\n", + "\n", + "**Prerequisites** (repo root): `make setup-keys && docker compose up -d`, then\n", + "`pip install highflame pyjwt cryptography`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "0c4f5a1a", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-01T04:10:53.180787Z", + "iopub.status.busy": "2026-09-01T04:10:53.180636Z", + "iopub.status.idle": "2026-09-01T04:10:53.339871Z", + "shell.execute_reply": "2026-09-01T04:10:53.339378Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "status='healthy' service='zeroid' timestamp=datetime.datetime(2026, 9, 1, 4, 10, 53, 335986, tzinfo=TzInfo(0)) uptime_ms=6828 | run id: 348a22\n" + ] + } + ], + "source": [ + "import time, uuid, jwt\n", + "from cryptography.hazmat.primitives.asymmetric import ec\n", + "from cryptography.hazmat.primitives import serialization\n", + "from highflame.zeroid import ZeroIDClient\n", + "\n", + "client = ZeroIDClient(base_url=\"http://localhost:8899\",\n", + " account_id=\"acct-sdk-demo\", project_id=\"proj-sdk-demo\")\n", + "run = uuid.uuid4().hex[:6]\n", + "print(client.health(), \"| run id:\", run)" + ] + }, + { + "cell_type": "markdown", + "id": "df509a15", + "metadata": {}, + "source": [ + "## 1 · Agent Registration Record — Layer 1 · The Passport (ODIS §6.1)\n", + "\n", + "`agents.register` creates the durable governance record and returns the bootstrap API key\n", + "in one call. The agent is born `unverified` with a stable WIMSE URI (ODIS's `agent_id`).\n", + "\n", + "> **SDK coverage note (0.3.17):** `client.credential_policies` can create issuance-ceiling\n", + "> policies, but there is no SDK parameter yet to *attach* one to an identity — the\n", + "> policy-gated fail-closed issuance demo therefore lives in the raw-HTTP companion. Worth\n", + "> an upstream SDK issue; the REST API supports it (`credential_policy_id`)." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ccacbb77", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-01T04:10:53.341024Z", + "iopub.status.busy": "2026-09-01T04:10:53.340914Z", + "iopub.status.idle": "2026-09-01T04:10:53.356109Z", + "shell.execute_reply": "2026-09-01T04:10:53.355623Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-348a22 | trust: unverified | status: active\n" + ] + } + ], + "source": [ + "reg = client.agents.register(name=\"Orchestrator\", external_id=f\"orch-{run}\",\n", + " sub_type=\"orchestrator\", created_by=\"demo-admin@example.com\")\n", + "orch = reg.identity\n", + "client.identities.update(orch.id, allowed_scopes=[\"data:read\", \"data:write\"])\n", + "print(orch.wimse_uri, \"| trust:\", orch.trust_level, \"| status:\", orch.status)" + ] + }, + { + "cell_type": "markdown", + "id": "da8bb026", + "metadata": {}, + "source": [ + "## 2 · Agent Runtime Credential — the Passport's output (ODIS §6.2, L1-05)\n", + "\n", + "Short-lived, scoped issuance from the bootstrap key. (The DPoP holder-binding variant is in\n", + "the raw companion; the SDK issues bearer tokens as of 0.3.17.)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "0979b715", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-01T04:10:53.357255Z", + "iopub.status.busy": "2026-09-01T04:10:53.357172Z", + "iopub.status.idle": "2026-09-01T04:10:53.365047Z", + "shell.execute_reply": "2026-09-01T04:10:53.364569Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Bearer | expires_in: 3600 s | scope: data:read data:write\n" + ] + } + ], + "source": [ + "token = client.tokens.issue_api_key(reg.api_key, scope=\"data:read data:write\")\n", + "print(token.token_type, \"| expires_in:\", token.expires_in, \"s | scope:\", token.scope)" + ] + }, + { + "cell_type": "markdown", + "id": "b3052c00", + "metadata": {}, + "source": [ + "## 3 · The ODIS-aware target — the Bridge's native mode (ODIS §2.5 / L2-15)\n", + "\n", + "ODIS §2.5 defines a native-mode target as one that independently validates the Agent\n", + "Runtime Credential and the active Delegation Record and enforces their **audience, holder\n", + "binding, attenuation, constraints, freshness, and revocation** semantics. This section\n", + "demonstrates the validation + attenuation/trust slice of that duty list; §5 covers\n", + "revocation. Audience enforcement is available (`tokens.verify(token, audience=...)`), and\n", + "holder-binding verification at the target (checking a DPoP proof against `cnf.jkt`) is what\n", + "ZeroID's Go `pkg/dpop` provides resource servers — not yet surfaced in the Python SDK.\n", + "\n", + "`tokens.verify()` performs the local JWKS validation — no\n", + "round-trip to the authorization server — and the typed guards are the enforcement. Note the\n", + "trust gate fires at the *target*, complementing the issuance-side gate the raw notebook\n", + "shows: the agent holds a perfectly valid token, and the tool server still refuses it until\n", + "the identity is trusted enough." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "562b02ec", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-01T04:10:53.366070Z", + "iopub.status.busy": "2026-09-01T04:10:53.365995Z", + "iopub.status.idle": "2026-09-01T04:10:53.372912Z", + "shell.execute_reply": "2026-09-01T04:10:53.372468Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "verified: spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-348a22 | delegated: False\n", + "refused (ZeroIDError): Missing required scope: 'admin:all'\n", + "refused (ZeroIDError): Insufficient trust level: required 'first_party', got 'unverified'\n" + ] + } + ], + "source": [ + "identity = client.tokens.verify(token.access_token) # local JWKS validation\n", + "print(\"verified:\", identity.sub, \"| delegated:\", identity.is_delegated())\n", + "\n", + "identity.require_scope(\"data:read\") # passes silently\n", + "for check in (lambda: identity.require_scope(\"admin:all\"),\n", + " lambda: identity.require_trust(\"first_party\")):\n", + " try:\n", + " check()\n", + " except Exception as e:\n", + " print(f\"refused ({type(e).__name__}):\", e)" + ] + }, + { + "cell_type": "markdown", + "id": "12ba8d36", + "metadata": {}, + "source": [ + "The identity gets promoted (here via the admin surface for brevity — the *attested*\n", + "promotion path, ODIS-L1-03/11, is demonstrated in the raw companion), and a fresh credential\n", + "now clears the target's trust gate:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "40ee32fa", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-01T04:10:53.374002Z", + "iopub.status.busy": "2026-09-01T04:10:53.373921Z", + "iopub.status.idle": "2026-09-01T04:10:53.382916Z", + "shell.execute_reply": "2026-09-01T04:10:53.382482Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "trust gate cleared: first_party\n" + ] + } + ], + "source": [ + "client.identities.update(orch.id, trust_level=\"first_party\")\n", + "token = client.tokens.issue_api_key(reg.api_key, scope=\"data:read data:write\")\n", + "identity = client.tokens.verify(token.access_token)\n", + "identity.require_trust(\"first_party\")\n", + "print(\"trust gate cleared:\", identity.trust_level)" + ] + }, + { + "cell_type": "markdown", + "id": "fd4b5c21", + "metadata": {}, + "source": [ + "## 4 · Delegation Record — Layer 2 · The Bridge (ODIS §6.3, Pillar 4, L2-05/06)\n", + "\n", + "The researcher registers with its own holder key, proves possession of it in the\n", + "`actor_token` (a self-signed ES256 assertion — the SDK doesn't mint these; five lines of\n", + "PyJWT do), and the orchestrator delegates via RFC 8693. Attenuation is **monotonic and\n", + "visible**: the researcher's ceiling is `data:read`, so requesting `data:read data:write`\n", + "yields a token silently narrowed to `data:read` — and a request where *nothing* survives\n", + "the intersection is refused outright. (With a credential policy attached, any\n", + "out-of-ceiling request errors instead of narrowing — the stricter mode is in the raw\n", + "companion.)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "bb180871", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-01T04:10:53.384075Z", + "iopub.status.busy": "2026-09-01T04:10:53.383999Z", + "iopub.status.idle": "2026-09-01T04:10:53.397494Z", + "shell.execute_reply": "2026-09-01T04:10:53.397073Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "granted scope (requested read+write): 'data:read'\n", + "sub: researcher-348a22 | is_delegated: True | act: {'sub': 'spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-348a22'} | depth: 1\n", + "empty intersection refused (APIError): [400] invalid_scope: requested scopes are not available for delegation\n" + ] + } + ], + "source": [ + "priv = ec.generate_private_key(ec.SECP256R1())\n", + "pub_pem = priv.public_key().public_bytes(serialization.Encoding.PEM,\n", + " serialization.PublicFormat.SubjectPublicKeyInfo).decode()\n", + "researcher = client.identities.create(\n", + " external_id=f\"researcher-{run}\", owner_user_id=\"demo-admin@example.com\",\n", + " name=\"Researcher\", identity_type=\"agent\", sub_type=\"tool_agent\",\n", + " trust_level=\"first_party\", allowed_scopes=[\"data:read\"], public_key_pem=pub_pem)\n", + "\n", + "now = int(time.time())\n", + "actor_token = jwt.encode({\"iss\": researcher.wimse_uri, \"sub\": researcher.wimse_uri,\n", + " \"aud\": [\"http://localhost:8899\"], \"iat\": now, \"exp\": now + 300},\n", + " priv, algorithm=\"ES256\")\n", + "\n", + "delegated = client.tokens.issue_token_exchange(\n", + " subject_token=token.access_token, actor_token=actor_token, scope=\"data:read data:write\")\n", + "print(\"granted scope (requested read+write):\", repr(delegated.scope))\n", + "\n", + "d_identity = client.tokens.verify(delegated.access_token)\n", + "print(\"sub:\", d_identity.sub.split(\"/\")[-1], \"| is_delegated:\", d_identity.is_delegated(),\n", + " \"| act:\", d_identity.act, \"| depth:\", d_identity.delegation_depth)\n", + "\n", + "try:\n", + " client.tokens.issue_token_exchange(subject_token=token.access_token,\n", + " actor_token=actor_token, scope=\"data:write admin:all\")\n", + "except Exception as e:\n", + " print(f\"empty intersection refused ({type(e).__name__}):\", e)" + ] + }, + { + "cell_type": "markdown", + "id": "27737340", + "metadata": {}, + "source": [ + "## 5 · Compromise signal, cascade — and what native mode must add — L1-12, L3-04/05\n", + "\n", + "A `critical` CAE signal against the orchestrator kills its credentials and cascades to the\n", + "researcher's delegated token. Then the ODIS-relevant subtlety: **local JWKS verification\n", + "cannot see revocation** — the delegated token still verifies offline, while introspection\n", + "reports it dead. ODIS's definition of an ODIS-aware target requires enforcing *revocation\n", + "semantics*, not just signatures: a native-mode target pairs local verification with\n", + "introspection, short cache windows, or the revocation event stream (`GET /signals/stream`).\n", + "This is exactly the trade the role-capability statement flags under L2-11/L3-04." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "61a4fccf", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-01T04:10:53.398515Z", + "iopub.status.busy": "2026-09-01T04:10:53.398433Z", + "iopub.status.idle": "2026-09-01T04:10:53.404846Z", + "shell.execute_reply": "2026-09-01T04:10:53.404343Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "introspection: False\n", + "local JWKS verify alone: True ← why native-mode targets must also check revocation state\n" + ] + } + ], + "source": [ + "client.signals.ingest(signal_type=\"anomalous_behavior\", source=\"sdk-demo\",\n", + " identity_id=orch.id, severity=\"critical\",\n", + " payload={\"reason\": \"prompt injection detected\"})\n", + "\n", + "print(\"introspection:\", client.tokens.introspect(delegated.access_token).active)\n", + "still_verifies = bool(client.tokens.verify(delegated.access_token).sub)\n", + "print(\"local JWKS verify alone:\", still_verifies, \"← why native-mode targets must also check revocation state\")" + ] + }, + { + "cell_type": "markdown", + "id": "9c25466e", + "metadata": {}, + "source": [ + "## What the SDK view added\n", + "\n", + "| ODIS concept | Where |\n", + "|---|---|\n", + "| Native-mode target validation (§2.5, L2-15) | §3 — local JWKS verify + typed guards |\n", + "| Target-side trust gating (complement to issuance gating) | §3 — `require_trust` refusing a valid token |\n", + "| Monotonic attenuation, both modes (L2-06) | §4 — silent narrowing + empty-intersection refusal |\n", + "| Revocation semantics are part of native mode (L3-04, L2-11) | §5 — verify-vs-introspect divergence |\n", + "\n", + "**SDK gaps observed (0.3.17), worth upstream issues (tracked in sdk#105):** no\n", + "`credential_policy_id` attachment on identities/agents, no attestation submit/verify, no\n", + "delegation-graph reads, and no DPoP proof support on token issuance — now load-bearing,\n", + "since the server can refuse proof-less issuance deployment-wide (`token.require_dpop`,\n", + "zeroid#304) — nor resource-server-side holder-binding (`cnf.jkt`/DPoP-proof) verification,\n", + "so a Python native-mode target can enforce signature, expiry, scope, trust, delegation,\n", + "audience, and (via introspection) revocation, but not yet holder binding. Everything above that the SDK lacks is\n", + "demonstrated over raw HTTP in the [main walkthrough](./odis-walkthrough.ipynb)." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/odis/odis-walkthrough.ipynb b/examples/odis/odis-walkthrough.ipynb new file mode 100644 index 00000000..e9371b27 --- /dev/null +++ b/examples/odis/odis-walkthrough.ipynb @@ -0,0 +1,800 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "89e18c13", + "metadata": {}, + "source": [ + "# ZeroID as ODIS Layers 1–2 — an executable walkthrough\n", + "\n", + "This notebook demonstrates [ZeroID](https://github.com/highflame-ai/zeroid) using the\n", + "vocabulary of the [ODIS draft](https://github.com/cosai-oasis/ws4-odis/blob/main/RFCs/ODIS.md)\n", + "(Open Delegation & Identity Standard, CoSAI/OASIS WS4). Each section is an ODIS concept;\n", + "each code cell is the ZeroID call that implements it. The committed outputs are from a real\n", + "run against a local instance.\n", + "\n", + "It is the executable companion to the\n", + "[role-capability statement](../../docs/odis/role-capability-statement.md), which maps every\n", + "ODIS requirement to code and tests — including the ones ZeroID does **not** meet.\n", + "\n", + "**Prerequisites** (from the repo root):\n", + "\n", + "```bash\n", + "make setup-keys # ES256 + RSA signing keys into ./keys\n", + "docker compose up -d # zeroid + postgres on localhost:8899\n", + "pip install requests pyjwt cryptography\n", + "```\n", + "\n", + "| ODIS term | In this notebook |\n", + "|---|---|\n", + "| Agent Registration Record (§6.1) | a ZeroID identity + its `CredentialPolicy` |\n", + "| Agent Runtime Credential (§6.2) | a short-lived DPoP-bound access token |\n", + "| Delegation Record (§6.3) | an RFC 8693 exchange: `act` chain, `delegation_depth`, `mission_id` |\n", + "| Attestation gate (L1-03/11) | trust level raised by a verified attestation, required by policy |\n", + "| Revocation / kill switch (L1-12, L3-04/05) | a CAE signal cascading down the delegation tree |\n", + "| Audit lineage (CC-01/02) | the delegation explorer's per-JTI graph |" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "822cd8f8", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-01T04:10:51.838276Z", + "iopub.status.busy": "2026-09-01T04:10:51.838024Z", + "iopub.status.idle": "2026-09-01T04:10:51.943361Z", + "shell.execute_reply": "2026-09-01T04:10:51.942881Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zeroid: healthy | run id: 1066b3\n" + ] + } + ], + "source": [ + "import base64, json, time, uuid, requests, jwt\n", + "from cryptography.hazmat.primitives.asymmetric import ec\n", + "from cryptography.hazmat.primitives import serialization\n", + "\n", + "BASE = \"http://localhost:8899\"\n", + "# ODIS trust_domain scoping: every control-plane call is tenant-scoped.\n", + "TENANT = {\"X-Account-ID\": \"acct-demo\", \"X-Project-ID\": \"proj-demo\", \"X-User-ID\": \"demo-admin@example.com\"}\n", + "\n", + "def api(method, path, **kw):\n", + " return requests.request(method, BASE + path, headers={**TENANT, **kw.pop(\"headers\", {})}, **kw)\n", + "\n", + "def b64u(b): return base64.urlsafe_b64encode(b).rstrip(b\"=\").decode()\n", + "\n", + "def gen_key():\n", + " \"\"\"An agent's holder key (ODIS: proof-of-possession key). Private half never leaves the agent.\"\"\"\n", + " k = ec.generate_private_key(ec.SECP256R1())\n", + " pem = k.public_key().public_bytes(serialization.Encoding.PEM,\n", + " serialization.PublicFormat.SubjectPublicKeyInfo).decode()\n", + " n = k.public_key().public_numbers()\n", + " jwk = {\"kty\": \"EC\", \"crv\": \"P-256\", \"x\": b64u(n.x.to_bytes(32, \"big\")), \"y\": b64u(n.y.to_bytes(32, \"big\"))}\n", + " return k, pem, jwk\n", + "\n", + "def claims_of(tok): return jwt.decode(tok, options={\"verify_signature\": False})\n", + "def show(obj): print(json.dumps(obj, indent=2, default=str))\n", + "\n", + "run = uuid.uuid4().hex[:6] # unique names per run, so the notebook is re-runnable\n", + "print(\"zeroid:\", requests.get(BASE + \"/health\").json()[\"status\"], \"| run id:\", run)" + ] + }, + { + "cell_type": "markdown", + "id": "da1d7cfd", + "metadata": {}, + "source": [ + "## 1 · Agent Registration Record — Layer 1 · The Passport (ODIS §6.1)\n", + "\n", + "ODIS's durable governance record for a logical agent (`agent_id`, lifecycle state, sponsor,\n", + "trust domain) is a ZeroID **identity**. Registration alone confers no authority: the agent is\n", + "born `unverified`, and its stable name is a SPIFFE/WIMSE URI\n", + "(`spiffe://{trust_domain}/{account}/{project}/{type}/{external_id}`) — ODIS's stable\n", + "`agent_id`, distinct from any runtime credential." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "f06d5077", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-01T04:10:51.944596Z", + "iopub.status.busy": "2026-09-01T04:10:51.944482Z", + "iopub.status.idle": "2026-09-01T04:10:51.973726Z", + "shell.execute_reply": "2026-09-01T04:10:51.973251Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"wimse_uri\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-1066b3\",\n", + " \"identity_type\": \"agent\",\n", + " \"trust_level\": \"unverified\",\n", + " \"status\": \"active\",\n", + " \"owner_user_id\": \"demo-admin@example.com\"\n", + "}\n" + ] + } + ], + "source": [ + "orch_key, orch_pem, orch_jwk = gen_key()\n", + "r = api(\"POST\", \"/api/v1/agents/register\", json={\n", + " \"name\": \"Orchestrator\", \"external_id\": f\"orch-{run}\",\n", + " \"identity_type\": \"agent\", \"sub_type\": \"orchestrator\",\n", + " \"allowed_scopes\": [\"data:read\", \"data:write\"],\n", + " \"created_by\": \"demo-admin@example.com\", # ODIS L1-10: accountable sponsor\n", + " \"public_key_pem\": orch_pem, # holder public key (L1-09)\n", + "})\n", + "orch = r.json()[\"identity\"]; orch_id = orch[\"id\"]; orch_apikey = r.json()[\"api_key\"]\n", + "show({k: orch[k] for k in (\"wimse_uri\", \"identity_type\", \"trust_level\", \"status\", \"owner_user_id\")})" + ] + }, + { + "cell_type": "markdown", + "id": "87c2fc37", + "metadata": {}, + "source": [ + "## 2 · Registration-record governance fields — Layer 1 · The Passport (ODIS §6.1)\n", + "\n", + "ODIS puts `permitted_delegation_modes`, lifetime bounds, and required assurance on the\n", + "registration record. ZeroID models these as a **CredentialPolicy** attached to the identity:\n", + "maximum TTL, permitted grant types, a scope ceiling, a **required trust level**, and a\n", + "**maximum delegation depth**. Note `client_credentials` stays in the allow-list — the\n", + "post-attestation bootstrap credential is itself policy-checked (everything fails closed,\n", + "including the server's own convenience issuance)." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "4870ab26", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-01T04:10:51.974895Z", + "iopub.status.busy": "2026-09-01T04:10:51.974813Z", + "iopub.status.idle": "2026-09-01T04:10:51.986830Z", + "shell.execute_reply": "2026-09-01T04:10:51.986370Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"name\": \"odis-demo-1066b3\",\n", + " \"max_ttl_seconds\": 3600,\n", + " \"required_trust_level\": \"first_party\",\n", + " \"max_delegation_depth\": 2\n", + "}\n" + ] + } + ], + "source": [ + "r = api(\"POST\", \"/api/v1/credential-policies\", json={\n", + " \"name\": f\"odis-demo-{run}\",\n", + " \"max_ttl_seconds\": 3600, # L1-05: bounded lifetime\n", + " \"allowed_grant_types\": [\"api_key\", \"token_exchange\", \"client_credentials\"],\n", + " \"allowed_scopes\": [\"data:read\", \"data:write\"], # scope ceiling\n", + " \"required_trust_level\": \"first_party\", # L1-11: attestation-gated authority\n", + " \"max_delegation_depth\": 2, # Pillar 4: bounded cascade\n", + "})\n", + "policy = r.json()\n", + "api(\"PATCH\", f\"/api/v1/identities/{orch_id}\", json={\"credential_policy_id\": policy[\"id\"]})\n", + "show({k: policy[k] for k in (\"name\", \"max_ttl_seconds\", \"required_trust_level\", \"max_delegation_depth\")})" + ] + }, + { + "cell_type": "markdown", + "id": "1574f8ca", + "metadata": {}, + "source": [ + "## 3 · No registration resolution, no authority — The Bridge refuses (ODIS-L2-14 + L1-11)\n", + "\n", + "ODIS: an Agent Runtime Credential is issued only after resolving to an **active** registration\n", + "that permits the request. The identity exists and is `active` — but its policy demands\n", + "`first_party` trust and the agent has never been attested. Issuance must refuse:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "2ace86f3", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-01T04:10:51.988046Z", + "iopub.status.busy": "2026-09-01T04:10:51.987964Z", + "iopub.status.idle": "2026-09-01T04:10:51.996088Z", + "shell.execute_reply": "2026-09-01T04:10:51.995598Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "400\n", + "{\n", + " \"error\": \"policy_violation\",\n", + " \"error_description\": \"credential policy violation: identity trust level \\\"unverified\\\" does not meet required level \\\"first_party\\\"\"\n", + "}\n" + ] + } + ], + "source": [ + "r = requests.post(BASE + \"/oauth2/token\", json={\"grant_type\": \"api_key\", \"api_key\": orch_apikey, \"scope\": \"data:read\"})\n", + "print(r.status_code); show(r.json())\n", + "assert r.status_code == 400" + ] + }, + { + "cell_type": "markdown", + "id": "695de017", + "metadata": {}, + "source": [ + "## 4 · Attestation raises trust — Layer 1 · The Passport (ODIS-L1-03 / L1-11)\n", + "\n", + "ODIS Layer 1 gates credentials on attestation. ZeroID's production verifier is a generic,\n", + "fail-closed **OIDC workload attestation** (GitHub Actions, GCP Workload Identity Federation,\n", + "Kubernetes projected SA tokens — configured per tenant via `AttestationPolicy`; see\n", + "`docs/attestation.md`). A local notebook has no cloud control plane, so this run uses the\n", + "**dev-stub** proof type (`image_hash`) that ships for exactly this purpose — the flow,\n", + "promotion semantics, and policy gates are identical. Verification promotes trust\n", + "exactly-once inside a transaction: `hardware → first_party`." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "f40299d7", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-01T04:10:51.997157Z", + "iopub.status.busy": "2026-09-01T04:10:51.997082Z", + "iopub.status.idle": "2026-09-01T04:10:52.013593Z", + "shell.execute_reply": "2026-09-01T04:10:52.013142Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "verify: 200\n", + "trust level now: first_party\n" + ] + } + ], + "source": [ + "att = api(\"POST\", \"/api/v1/attestation/submit\", json={\n", + " \"identity_id\": orch_id, \"level\": \"hardware\",\n", + " \"proof_type\": \"image_hash\", \"proof_value\": \"sha256:deadbeef\", # dev stub: local demo only\n", + "}).json()\n", + "r = api(\"POST\", \"/api/v1/attestation/verify\", json={\"attestation_id\": att[\"id\"]})\n", + "print(\"verify:\", r.status_code)\n", + "print(\"trust level now:\", api(\"GET\", f\"/api/v1/identities/{orch_id}\").json()[\"trust_level\"])" + ] + }, + { + "cell_type": "markdown", + "id": "4908876e", + "metadata": {}, + "source": [ + "## 5 · Agent Runtime Credential — the Passport's output (ODIS §6.2, L1-05 / L1-09)\n", + "\n", + "Short-lived, holder-bound, issued only post-attestation. The **DPoP proof** (RFC 9449) is\n", + "signed by the agent's holder key; the issued token carries `cnf.jkt` (the key thumbprint),\n", + "making it proof-of-possession rather than bearer. The `sub` is the WIMSE URI — logical agent\n", + "and runtime credential are distinct objects, which is ODIS's §1.3 identifier/credential\n", + "separation.\n", + "\n", + "Deployment note: the Bearer *fallback* (omit the header, get an unbound token) can be\n", + "closed deployment-wide with `token.require_dpop: true` in zeroid.yaml — issuance without a\n", + "proof is then refused with `invalid_dpop_proof` and the AS metadata advertises\n", + "`dpop_bound_access_tokens_required` (RFC 9449 §5.1). This notebook's compose config keeps\n", + "the default (off) so the Bearer comparison in §6's delegation flow stays visible." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "861d848b", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-01T04:10:52.014749Z", + "iopub.status.busy": "2026-09-01T04:10:52.014679Z", + "iopub.status.idle": "2026-09-01T04:10:52.029918Z", + "shell.execute_reply": "2026-09-01T04:10:52.029457Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "token_type: DPoP | expires_in: 3600 s\n", + "{\n", + " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-1066b3\",\n", + " \"trust_level\": \"first_party\",\n", + " \"mission_id\": \"e1c42e24-bf28-4430-b649-e747cde5523d\",\n", + " \"jti\": \"e1c42e24-bf28-4430-b649-e747cde5523d\"\n", + "}\n", + "cnf: {'jkt': '1CQncC-hdKz54kXrbZMqS2G6nKKwB5vLHiaEpPE6Jn0'}\n" + ] + } + ], + "source": [ + "def dpop_proof(key, jwk, htm, htu):\n", + " return jwt.encode({\"jti\": str(uuid.uuid4()), \"htm\": htm, \"htu\": htu, \"iat\": int(time.time())},\n", + " key, algorithm=\"ES256\", headers={\"typ\": \"dpop+jwt\", \"jwk\": jwk})\n", + "\n", + "proof = dpop_proof(orch_key, orch_jwk, \"POST\", f\"{BASE}/oauth2/token\")\n", + "r = requests.post(BASE + \"/oauth2/token\", headers={\"DPoP\": proof},\n", + " json={\"grant_type\": \"api_key\", \"api_key\": orch_apikey, \"scope\": \"data:read data:write\"})\n", + "tok = r.json(); orch_token = tok[\"access_token\"]\n", + "c = claims_of(orch_token)\n", + "print(\"token_type:\", tok[\"token_type\"], \"| expires_in:\", tok[\"expires_in\"], \"s\")\n", + "show({k: c[k] for k in (\"sub\", \"trust_level\", \"mission_id\", \"jti\")}); print(\"cnf:\", c[\"cnf\"])" + ] + }, + { + "cell_type": "markdown", + "id": "2675b3a7", + "metadata": {}, + "source": [ + "A replayed proof must be rejected — the `jti` ledger is atomic (ODIS's replay concern\n", + "under L1-09; ZeroID's `TestDPoPReplayRejected` class of behavior, live):" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "09970b7e", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-01T04:10:52.030944Z", + "iopub.status.busy": "2026-09-01T04:10:52.030868Z", + "iopub.status.idle": "2026-09-01T04:10:52.035382Z", + "shell.execute_reply": "2026-09-01T04:10:52.034966Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "400\n", + "{\n", + " \"error\": \"invalid_dpop_proof\",\n", + " \"error_description\": \"dpop: proof jti has already been observed within the freshness window (dpop_replay_detected)\"\n", + "}\n" + ] + } + ], + "source": [ + "r = requests.post(BASE + \"/oauth2/token\", headers={\"DPoP\": proof},\n", + " json={\"grant_type\": \"api_key\", \"api_key\": orch_apikey, \"scope\": \"data:read\"})\n", + "print(r.status_code); show(r.json())\n", + "assert r.status_code == 400" + ] + }, + { + "cell_type": "markdown", + "id": "770f1636", + "metadata": {}, + "source": [ + "## 6 · Delegation Record — Layer 2 · The Bridge (ODIS §6.3, Pillar 4, L2-01/05/06)\n", + "\n", + "The orchestrator delegates to a sub-agent via **RFC 8693 token exchange**. The sub-agent\n", + "proves possession of its own holder key (`actor_token`), and the granted scope is the\n", + "**three-way intersection**: requested ∩ what the orchestrator holds ∩ the sub-agent's policy\n", + "ceiling. The result carries ODIS Delegation-Record semantics as claims: `act` (the delegating\n", + "principal), `delegation_depth` (monotonic, capped by policy), and `mission_id` (chain lineage,\n", + "inherited from the root credential). Child expiry is clamped to the parent's — a child cannot\n", + "outlive its parent." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "1bfed207", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-01T04:10:52.036470Z", + "iopub.status.busy": "2026-09-01T04:10:52.036388Z", + "iopub.status.idle": "2026-09-01T04:10:52.055581Z", + "shell.execute_reply": "2026-09-01T04:10:52.055262Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/researcher-1066b3\",\n", + " \"act\": {\n", + " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-1066b3\"\n", + " },\n", + " \"delegation_depth\": 1,\n", + " \"mission_id\": \"e1c42e24-bf28-4430-b649-e747cde5523d\",\n", + " \"jti\": \"0ab9c9d8-c71a-4a43-972b-7c754b0e2e1c\"\n", + "}\n" + ] + } + ], + "source": [ + "sub_key, sub_pem, _ = gen_key()\n", + "r = api(\"POST\", \"/api/v1/agents/register\", json={\n", + " \"name\": \"Researcher\", \"external_id\": f\"researcher-{run}\", \"identity_type\": \"agent\",\n", + " \"sub_type\": \"tool_agent\", \"allowed_scopes\": [\"data:read\"],\n", + " \"created_by\": \"demo-admin@example.com\", \"public_key_pem\": sub_pem,\n", + " \"credential_policy_id\": policy[\"id\"],\n", + "})\n", + "researcher = r.json()[\"identity\"]\n", + "att2 = api(\"POST\", \"/api/v1/attestation/submit\", json={\"identity_id\": researcher[\"id\"], \"level\": \"hardware\",\n", + " \"proof_type\": \"image_hash\", \"proof_value\": \"sha256:cafef00d\"}).json()\n", + "api(\"POST\", \"/api/v1/attestation/verify\", json={\"attestation_id\": att2[\"id\"]})\n", + "\n", + "actor_token = jwt.encode({\"iss\": researcher[\"wimse_uri\"], \"sub\": researcher[\"wimse_uri\"], \"aud\": BASE,\n", + " \"iat\": int(time.time()), \"exp\": int(time.time()) + 300}, sub_key, algorithm=\"ES256\")\n", + "r = requests.post(BASE + \"/oauth2/token\", json={\n", + " \"grant_type\": \"urn:ietf:params:oauth:grant-type:token-exchange\",\n", + " \"subject_token\": orch_token, \"actor_token\": actor_token, \"scope\": \"data:read\"})\n", + "delegated = r.json()[\"access_token\"]; dc = claims_of(delegated)\n", + "show({k: dc[k] for k in (\"sub\", \"act\", \"delegation_depth\", \"mission_id\", \"jti\")})" + ] + }, + { + "cell_type": "markdown", + "id": "93d3a37a", + "metadata": {}, + "source": [ + "Attenuation is monotonic and fails closed: the researcher's ceiling is `data:read`,\n", + "so a delegation that tries to smuggle `data:write` (which the *orchestrator* holds) is\n", + "refused — a sub-agent can never become a privilege-amplification point (ODIS-L2-06's core\n", + "demand):" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "e8dd1999", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-01T04:10:52.056777Z", + "iopub.status.busy": "2026-09-01T04:10:52.056701Z", + "iopub.status.idle": "2026-09-01T04:10:52.061831Z", + "shell.execute_reply": "2026-09-01T04:10:52.061389Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "400\n", + "{\n", + " \"error\": \"insufficient_scope\",\n", + " \"error_description\": \"one or more requested scopes are not permitted for this identity: \\\"data:write\\\" not in allowed_scopes\"\n", + "}\n" + ] + } + ], + "source": [ + "r = requests.post(BASE + \"/oauth2/token\", json={\n", + " \"grant_type\": \"urn:ietf:params:oauth:grant-type:token-exchange\",\n", + " \"subject_token\": orch_token, \"actor_token\": actor_token, \"scope\": \"data:read data:write\"})\n", + "print(r.status_code); show(r.json())\n", + "assert r.status_code == 400" + ] + }, + { + "cell_type": "markdown", + "id": "383d6788", + "metadata": {}, + "source": [ + "## 7 · Bounded asynchronous human authorization — Layer 2 · The Bridge (ODIS-L2-02, Pillar 1)\n", + "\n", + "ODIS Pillar 1: agents need delegated human authority **without dragging the human into the\n", + "execution loop** — the Browser Trap. The Bridge's answer is CIBA (OpenID Client-Initiated\n", + "Backchannel Authentication): the agent requests authority out-of-band, a human approves\n", + "asynchronously (here via the admin surface; ping/push notifier modes exist), and only then\n", + "does polling yield a token. The `binding_message` shows the human exactly what they are\n", + "approving, and the issued token's `sub` is the **approving human** with the grant recorded\n", + "as `token_exchange: \"ciba\"` — originating-principal attribution, ODIS-CC-02." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "115c1a82", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-01T04:10:52.062880Z", + "iopub.status.busy": "2026-09-01T04:10:52.062812Z", + "iopub.status.idle": "2026-09-01T04:10:52.379962Z", + "shell.execute_reply": "2026-09-01T04:10:52.378435Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "bc-authorize: 200\n", + "{\n", + " \"auth_req_id\": \"5YcZ0SWxqb02oKzJi1dRTaHNnC4jOKyDJVt08J5bYF8\",\n", + " \"expires_in\": 300,\n", + " \"interval\": 5\n", + "}\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "poll before approval: authorization_pending\n", + "after approval — expires_in: 900 s\n", + "{\n", + " \"sub\": \"user-alice-001\",\n", + " \"token_exchange\": \"ciba\"\n", + "}\n" + ] + } + ], + "source": [ + "alice_agent = api(\"POST\", \"/api/v1/identities\", json={\n", + " \"external_id\": f\"alice-proxy-{run}\", \"owner_user_id\": \"alice@example.com\",\n", + " \"identity_type\": \"agent\", \"sub_type\": \"human_proxy\",\n", + " \"allowed_scopes\": [\"data:read\"], \"trust_level\": \"first_party\"}).json()\n", + "cl = api(\"POST\", \"/api/v1/oauth/clients\", json={\n", + " \"client_id\": f\"alice-proxy-{run}\", \"name\": \"Alice's proxy agent\", \"confidential\": True,\n", + " \"identity_id\": alice_agent[\"id\"], \"grant_types\": [\"urn:openid:params:grant-type:ciba\"],\n", + " \"scopes\": [\"data:read\"]}).json()\n", + "\n", + "r = requests.post(BASE + \"/oauth2/bc-authorize\", json={\n", + " \"client_id\": cl[\"client\"][\"client_id\"], \"client_secret\": cl[\"client_secret\"],\n", + " \"account_id\": TENANT[\"X-Account-ID\"], \"project_id\": TENANT[\"X-Project-ID\"],\n", + " \"login_hint\": \"alice@example.com\", \"scope\": \"data:read\",\n", + " \"binding_message\": \"Agent requests read access to the quarterly dataset\"})\n", + "auth_req = r.json(); print(\"bc-authorize:\", r.status_code); show(auth_req)\n", + "\n", + "poll = lambda: requests.post(BASE + \"/oauth2/token\", json={\n", + " \"grant_type\": \"urn:openid:params:grant-type:ciba\", \"auth_req_id\": auth_req[\"auth_req_id\"],\n", + " \"client_id\": cl[\"client\"][\"client_id\"], \"client_secret\": cl[\"client_secret\"]})\n", + "print(\"poll before approval:\", poll().json()[\"error\"])\n", + "\n", + "api(\"POST\", f\"/api/v1/oauth2/bc-authorize/{auth_req['auth_req_id']}/approve\",\n", + " json={\"subject_id\": \"user-alice-001\", \"subject_email\": \"alice@example.com\", \"subject_name\": \"Alice\"})\n", + "ciba_token = poll().json()\n", + "cc2 = claims_of(ciba_token[\"access_token\"])\n", + "print(\"after approval — expires_in:\", ciba_token[\"expires_in\"], \"s\")\n", + "show({k: cc2.get(k) for k in (\"sub\", \"token_exchange\", \"scope\") if cc2.get(k)})" + ] + }, + { + "cell_type": "markdown", + "id": "f4e7dde9", + "metadata": {}, + "source": [ + "## 8 · Confirmed compromise → cascade revocation — ODIS-L1-12, L3-04 / L3-05\n", + "\n", + "ODIS: a confirmed compromise signal must revoke the affected credential and everything\n", + "derived from it. Ingesting a `critical` CAE signal against the **orchestrator** revokes its\n", + "credentials and cascades down the `parent_jti` tree — the researcher's *delegated* token dies\n", + "with its parent, and we time how long the whole thing takes:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "6d7f8cd0", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-01T04:10:52.384050Z", + "iopub.status.busy": "2026-09-01T04:10:52.383562Z", + "iopub.status.idle": "2026-09-01T04:10:52.403032Z", + "shell.execute_reply": "2026-09-01T04:10:52.402133Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "delegated token after cascade: {'active': False} (13 ms signal→dead)\n" + ] + } + ], + "source": [ + "t0 = time.time()\n", + "api(\"POST\", \"/api/v1/signals/ingest\", json={\n", + " \"identity_id\": orch_id, \"signal_type\": \"anomalous_behavior\", \"severity\": \"critical\",\n", + " \"source\": \"notebook-demo\", \"payload\": {\"reason\": \"prompt injection detected\"}})\n", + "child_state = requests.post(BASE + \"/oauth2/token/introspect\", json={\"token\": delegated}).json()\n", + "print(f\"delegated token after cascade: {child_state} ({(time.time()-t0)*1000:.0f} ms signal→dead)\")\n", + "assert child_state == {\"active\": False}" + ] + }, + { + "cell_type": "markdown", + "id": "59362c1d", + "metadata": {}, + "source": [ + "## 9 · Audit lineage survives the kill — ODIS-CC-01 / CC-02\n", + "\n", + "Revoked credentials are retained past expiry precisely so the delegation graph remains\n", + "walkable for forensics. The per-JTI lineage shows both identities (dual-identity audit),\n", + "the scope attenuation at each hop (`scopes_in` → `scopes_out`), and *why* each edge died:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "349a6c07", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-01T04:10:52.406067Z", + "iopub.status.busy": "2026-09-01T04:10:52.405828Z", + "iopub.status.idle": "2026-09-01T04:10:52.416704Z", + "shell.execute_reply": "2026-09-01T04:10:52.415670Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "chain: orch-1066b3 → researcher-1066b3\n", + "[\n", + " {\n", + " \"grant_type\": \"api_key\",\n", + " \"delegation_depth\": 0,\n", + " \"scopes_in\": [\n", + " \"data:read\",\n", + " \"data:write\"\n", + " ],\n", + " \"scopes_out\": [\n", + " \"data:read\",\n", + " \"data:write\"\n", + " ],\n", + " \"attenuated\": [],\n", + " \"is_revoked\": true,\n", + " \"revoke_reason\": \"auto-revoked by CAE signal fa2bd26d-2abe-4de9-b922-5f5075720674 (severity: critical)\"\n", + " },\n", + " {\n", + " \"grant_type\": \"token_exchange\",\n", + " \"delegation_depth\": 1,\n", + " \"scopes_in\": [\n", + " \"data:read\",\n", + " \"data:write\"\n", + " ],\n", + " \"scopes_out\": [\n", + " \"data:read\"\n", + " ],\n", + " \"attenuated\": [\n", + " \"data:write\"\n", + " ],\n", + " \"is_revoked\": true,\n", + " \"revoke_reason\": \"auto-revoked by CAE signal fa2bd26d-2abe-4de9-b922-5f5075720674 (severity: critical)\"\n", + " }\n", + "]\n" + ] + } + ], + "source": [ + "g = api(\"GET\", f\"/api/v1/delegations/by-jti/{dc['jti']}\").json()\n", + "print(\"chain:\", \" → \".join(n[\"wimse_uri\"].split(\"/\")[-1] for n in g[\"nodes\"]))\n", + "show([{k: e[k] for k in (\"grant_type\", \"delegation_depth\", \"scopes_in\", \"scopes_out\",\n", + " \"attenuated\", \"is_revoked\", \"revoke_reason\")} for e in g[\"edges\"]])" + ] + }, + { + "cell_type": "markdown", + "id": "6fe535aa", + "metadata": {}, + "source": [ + "## 10 · The sponsor leaves — offboarding kill switch (ODIS-L1-06 / L1-10 / L3-05)\n", + "\n", + "ODIS-L1-10: every agent has an accountable human sponsor, and sponsor lifecycle events must\n", + "trigger de-provisioning. Alice — the human who just approved her proxy agent's authority in\n", + "§7 — leaves the organization. One call deactivates every identity she owned and\n", + "cascade-revokes their credentials, including the CIBA-approved token: the kill switch\n", + "(L3-05) keyed on the human, not the agent." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "a2e5852a", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-01T04:10:52.419903Z", + "iopub.status.busy": "2026-09-01T04:10:52.419704Z", + "iopub.status.idle": "2026-09-01T04:10:52.434367Z", + "shell.execute_reply": "2026-09-01T04:10:52.433819Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"$schema\": \"http://localhost:8899/schemas/OffboardByOwnerOutputBody.json\",\n", + " \"identities_deactivated\": 1,\n", + " \"credentials_revoked\": 0\n", + "}\n", + "alice's CIBA-approved token: {'active': False}\n" + ] + } + ], + "source": [ + "r = api(\"POST\", \"/api/v1/identities/offboard-by-owner\", json={\"owner_user_id\": \"alice@example.com\"})\n", + "show(r.json())\n", + "print(\"alice's CIBA-approved token:\",\n", + " requests.post(BASE + \"/oauth2/token/introspect\", json={\"token\": ciba_token[\"access_token\"]}).json())" + ] + }, + { + "cell_type": "markdown", + "id": "ab0d54b3", + "metadata": {}, + "source": [ + "## What you just saw, in ODIS terms\n", + "\n", + "| ODIS concept | Layer | Demonstrated |\n", + "|---|---|---|\n", + "| §6.1 Agent Registration Record; §1.3 identifier ≠ credential; CC-05 governed creation; L1-10 accountable sponsor | The Passport | §1 — registered identity, WIMSE `agent_id`, no authority conferred |\n", + "| §6.1 governance fields: lifetime bound, permitted grants, required trust, delegation-depth cap | The Passport | §2 — `CredentialPolicy` attached to the registration record |\n", + "| L2-14 registration resolution before authority; L1-11 fail closed | The Bridge | §3 — issuance refused for an unattested identity |\n", + "| L1-03 / L1-11 attestation-bootstrapped trust | The Passport | §4 — verification promoted trust, atomically |\n", + "| §6.2 Agent Runtime Credential; L1-05 / L1-09 proof-of-possession | The Passport | §5 — DPoP-bound token, replay rejected (`token.require_dpop` makes binding mandatory deployment-wide) |\n", + "| §6.3 Delegation Record; Pillar 4; L2-01 / L2-05 / L2-06 monotonic attenuation | The Bridge | §6 — `act` chain, depth, escalation refused |\n", + "| L2-02 bounded async authorization; Pillar 1 delegated principal identity | The Bridge | §7 — CIBA approval with binding message; token `sub` = the human |\n", + "| L1-12 / L3-04 / L3-05 compromise signal, cascade | (Router-adjacent) | §8 — critical signal killed the whole tree in milliseconds |\n", + "| CC-01 / CC-02 dual-identity audit lineage | cross-cutting | §9 — the graph outlives the credentials |\n", + "| L1-06 / L1-10 / L3-05 sponsor offboarding, kill switch | The Passport | §10 — the human leaves; every identity and credential they sponsored dies |\n", + "\n", + "**What ZeroID deliberately does *not* demonstrate** — software/supply-chain attestation\n", + "(L1-02/08), bridge-mode provider adapters (L2-08..10), presenter isolation (L2-13), and\n", + "velocity limits (L3-03) are open gaps, documented with the same candor in the\n", + "[role-capability statement](../../docs/odis/role-capability-statement.md). That document is\n", + "the map; this notebook is the territory." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 5db84d093662be07599f2b8a9a3ba0ebfdce7824 Mon Sep 17 00:00:00 2001 From: Sharath Rajasekar Date: Thu, 3 Sep 2026 16:23:58 -0700 Subject: [PATCH 02/14] devops: execute ODIS notebooks in notebook-check and lint outputs for credential material Wires both ODIS walkthrough notebooks into the existing notebook-check job so their committed outputs can't rot silently, and adds a grep gate that fails the PR if any committed notebook output embeds raw JWTs, zeroid API keys, or private-key blocks. Co-Authored-By: Claude Fable 5 --- .github/workflows/pr-check.yml | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 2679bd55..b0c782e5 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -259,11 +259,22 @@ jobs: - name: Install notebook execution deps shell: bash - run: pip install --quiet --upgrade pytest nbmake ipykernel highflame cryptography "PyJWT>=2.8" + run: pip install --quiet --upgrade pytest nbmake ipykernel highflame cryptography "PyJWT>=2.8" requests - - name: Execute the quickstart notebook against the live server + # Committed outputs must never contain credential material: raw JWTs, + # zeroid API keys, or private keys. Guards the ODIS walkthroughs' policy + # of printing decoded claims / selected fields only. + - name: Lint committed notebook outputs for credential material shell: bash - run: pytest --nbmake --nbmake-timeout=300 examples/zeroid_quickstart.ipynb -v + run: | + if grep -RInE 'eyJ[A-Za-z0-9_-]{20,}|zid_sk_[A-Za-z0-9]|BEGIN[A-Z ]*PRIVATE KEY' \ + examples/*.ipynb examples/odis/*.ipynb; then + echo "::error::credential-looking material found in committed notebook(s)"; exit 1 + fi + + - name: Execute the quickstart + ODIS notebooks against the live server + shell: bash + run: pytest --nbmake --nbmake-timeout=300 examples/zeroid_quickstart.ipynb examples/odis/odis-walkthrough.ipynb examples/odis/odis-walkthrough-sdk.ipynb -v - name: Dump zeroid logs on failure if: failure() From afb373b04dcf13036b12ddd8c6f987899c63330d Mon Sep 17 00:00:00 2001 From: Sharath Rajasekar Date: Thu, 3 Sep 2026 16:23:58 -0700 Subject: [PATCH 03/14] docs: address review findings on ODIS notebooks and statement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - §10 now explains credentials_revoked counter semantics (final-sweep stragglers, 0 is healthy) and proves actual revocation via the audit graph's revoke_reason=identity_deactivated - SDK notebook: pin highflame==0.3.17, up-front require_dpop:false requirement (sdk#105), audience-enforcement demo (verify(audience=...) pass + refusal), inline revocation-blindness caution, trust-minted-into- token explanation with the demotion corollary - both notebooks: local dev trust-model callout (unauthenticated admin plane, body-named CIBA approval subject), claims_of display-only caveat, stale-volume hint at the §7 point of failure - README: per-notebook configuration matrix (which require_dpop setting each runs under), SDK version pin, no-credential-material statement - statement: L1-09 cross-references the per-notebook config matrix - both notebooks re-executed against a fresh stack; committed outputs real Co-Authored-By: Claude Fable 5 --- docs/odis/role-capability-statement.md | 2 +- examples/odis/README.md | 21 ++ examples/odis/odis-walkthrough-sdk.ipynb | 180 +++++++++++------ examples/odis/odis-walkthrough.ipynb | 235 +++++++++++++---------- 4 files changed, 279 insertions(+), 159 deletions(-) diff --git a/docs/odis/role-capability-statement.md b/docs/odis/role-capability-statement.md index 9cb652ec..168b6450 100644 --- a/docs/odis/role-capability-statement.md +++ b/docs/odis/role-capability-statement.md @@ -129,7 +129,7 @@ style REV fill:#f0f0f0,color:#333,stroke:#999 | **ODIS-L1-06** Provisioning & De-provisioning | **Meets** | Lifecycle states `discovered→pending→active→suspended→deactivated/expired` (`domain/identity.go` `CanTransitionTo`); adopt/dismiss (`internal/handler/identity.go`); offboard-by-owner cascade (`internal/service/identity.go` `OffboardOwner`, DB `revoke_credentials_by_owner_cascade`); `tests/integration/discovery_lifecycle_test.go` | Satisfied via the "equivalent lifecycle-management mechanisms" clause — SCIM protocol endpoints themselves are not implemented. | | **ODIS-L1-07** Federated Trust *(Extended)* | **Meets** | Direct external-issuer OIDC federation: `external_issuers` config (`domain/external_issuer.go`), verification `internal/service/oauth_external_idp.go` (`subject_token_type=…:id_token`), upstream `iss` propagated as `user_id_iss`; `TestExternalIDTokenFederation_EndToEnd`, `_KeyRotation`, `_CrossTenantRejected` | Deployer-configured trust anchors; no automatic federation discovery. A trusted-service broker path also exists (`ExternalPrincipalExchange`) and is documented as the lossier fallback. | | **ODIS-L1-08** Trusted Distribution | **Gap** | — | No supply-chain verification before identity issuance. Pairs with L1-02. | -| **ODIS-L1-09** Holder-of-Key Authentication | **Meets (via configuration)** | DPoP with atomic jti replay ledger (`pkg/dpop/verifier.go`, `TestDPoPReplayRejected`); refresh tokens key-bound (`TestDPoPRefreshBoundWithDifferentKeyRejected`); binding propagates through delegation (`TestDPoPTokenExchange_PropagatesBindingToSubAgent`); WIMSE single-use nonce-bound proof tokens (`internal/service/proof.go`); **enforcement switch**: `token.require_dpop` refuses proof-less issuance with `invalid_dpop_proof` on every grant and advertises `dpop_bound_access_tokens_required` per RFC 9449 §5.1 (`TestRequireDPoPRefusesProoflessIssuance`, `TestRequireDPoPAdvertisedInASMetadata`) | The conformant posture is `token.require_dpop: true` (default off preserves Bearer opt-in): every issued credential is then proof-of-possession-bound. Client-side note: the Python SDK (0.3.17) does not yet construct DPoP proofs (tracked upstream, sdk#105), so a require_dpop deployment currently serves raw-HTTP/self-signing clients — the notebook demonstrates proof construction in a few lines of PyJWT. | +| **ODIS-L1-09** Holder-of-Key Authentication | **Meets (via configuration)** | DPoP with atomic jti replay ledger (`pkg/dpop/verifier.go`, `TestDPoPReplayRejected`); refresh tokens key-bound (`TestDPoPRefreshBoundWithDifferentKeyRejected`); binding propagates through delegation (`TestDPoPTokenExchange_PropagatesBindingToSubAgent`); WIMSE single-use nonce-bound proof tokens (`internal/service/proof.go`); **enforcement switch**: `token.require_dpop` refuses proof-less issuance with `invalid_dpop_proof` on every grant and advertises `dpop_bound_access_tokens_required` per RFC 9449 §5.1 (`TestRequireDPoPRefusesProoflessIssuance`, `TestRequireDPoPAdvertisedInASMetadata`) | The conformant posture is `token.require_dpop: true` (default off preserves Bearer opt-in): every issued credential is then proof-of-possession-bound. Client-side note: the Python SDK (0.3.17) does not yet construct DPoP proofs (tracked upstream, sdk#105), so a require_dpop deployment currently serves raw-HTTP/self-signing clients — the notebook demonstrates proof construction in a few lines of PyJWT. Consequently the SDK companion notebook runs under the default `require_dpop: false`; each notebook's required configuration is stated in `examples/odis/README.md`. | | **ODIS-L1-10** Accountable Sponsor | **Meets** | `owner_user_id` verified against the tenant directory (CAP-DSC-004, `internal/service/identity.go`); offboard-by-owner cascade; ownerless as a surfaced posture signal | ODIS's administrative *drain* state is not implemented — lifecycle events revoke rather than drain. | | **ODIS-L1-11** Attestation-Bootstrapped Trust | **Meets (via configuration)** | Attestation raises `trust_level` exactly-once in-transaction (`internal/service/attestation.go` `trustLevelForAttestation`); `CredentialPolicy.RequiredTrustLevel`/`RequiredAttestation` gate issuance (`EnforcePolicy` checks 4–5); expiry demotes (`TestExpiredAttestationNoLongerSatisfiesPolicy`) | The conformant posture: `RequiredTrustLevel`/`RequiredAttestation` on the identity's `CredentialPolicy` makes issuance depend on verified attestation, fail-closed (demonstrated live in the companion notebook, §3–§4). Tenants that omit the requirement have chosen a non-ODIS posture; the enforcement machinery is shipped and tested. | | **ODIS-L1-12** Runtime Security State | **Meets** | Signal ingest `POST /signals/ingest` (`internal/handler/signal.go`); severity-driven revocation with cascade (`TestCAECriticalSignalRevokesCredential`, `TestCAESignalCascadesRevocationToChildren`) | Signal schema is ZeroID-local, not CAEP event-type URIs (see `COMPLIANCE.md`'s honest SSF/CAEP row). | diff --git a/examples/odis/README.md b/examples/odis/README.md index 135d8521..1287e3e2 100644 --- a/examples/odis/README.md +++ b/examples/odis/README.md @@ -36,6 +36,27 @@ pip install requests pyjwt cryptography jupyter jupyter notebook examples/odis/odis-walkthrough.ipynb ``` +The SDK notebook additionally needs `pip install "highflame==0.3.17"` — the +version its committed outputs were generated against. + +## Which configuration each notebook runs under + +Both notebooks execute against the stock compose deployment — in particular +`token.require_dpop: false`, the default: + +- The raw-HTTP walkthrough **chooses** the default so §5 can show the Bearer + fallback and the configuration switch that closes it; every DPoP behavior + (holder binding, replay rejection) is still demonstrated live. +- The SDK walkthrough **requires** the default: the Python SDK (0.3.17) cannot + construct DPoP proofs yet (highflame-sdk#105), so under `require_dpop: true` + — the hardened posture the role-capability statement grades L1-09 against — + its issuance calls are refused with `invalid_dpop_proof`. + +The committed outputs contain no credential material: cells print decoded +claims and selected fields, never raw tokens, API keys, or private keys. CI +enforces this with a credential-material lint and re-executes both notebooks +against a fresh server on every PR (`highflame-notebook-check`). + Upgrading an existing checkout? Tenant default credential policies are created once and don't self-heal: a postgres volume from before the CIBA grant joined the defaults will refuse §7's bound-client redemption diff --git a/examples/odis/odis-walkthrough-sdk.ipynb b/examples/odis/odis-walkthrough-sdk.ipynb index c2065409..0d4e71b7 100644 --- a/examples/odis/odis-walkthrough-sdk.ipynb +++ b/examples/odis/odis-walkthrough-sdk.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "ce5cb0d8", + "id": "e9f5a59e", "metadata": {}, "source": [ "# ZeroID as ODIS Layers 1–2 — the SDK companion\n", @@ -20,19 +20,32 @@ " tool server calls before executing anything.\n", "\n", "**Prerequisites** (repo root): `make setup-keys && docker compose up -d`, then\n", - "`pip install highflame pyjwt cryptography`." + "`pip install \"highflame==0.3.17\" pyjwt cryptography` — 0.3.17 is the SDK version these\n", + "committed outputs were generated against.\n", + "\n", + "> **Configuration note (ODIS-L1-09):** this notebook requires the compose default\n", + "> `token.require_dpop: false`. The Python SDK cannot construct DPoP proofs yet\n", + "> (highflame-sdk#105), so under the hardened configuration the role-capability statement\n", + "> grades as L1-09 *Meets (via configuration)*, every issuance call below is refused with\n", + "> `invalid_dpop_proof`. The DPoP-bound path is demonstrated over raw HTTP in the main\n", + "> walkthrough.\n", + ">\n", + "> **Local dev trust model:** admin-plane SDK calls (`identities.update`, `signals.ingest`,\n", + "> …) authenticate here only by client-supplied tenant IDs — a dev-mode convenience.\n", + "> Production fronts the admin plane with authenticated sessions and derives tenancy from\n", + "> verified claims." ] }, { "cell_type": "code", "execution_count": 1, - "id": "0c4f5a1a", + "id": "59fad93b", "metadata": { "execution": { - "iopub.execute_input": "2026-09-01T04:10:53.180787Z", - "iopub.status.busy": "2026-09-01T04:10:53.180636Z", - "iopub.status.idle": "2026-09-01T04:10:53.339871Z", - "shell.execute_reply": "2026-09-01T04:10:53.339378Z" + "iopub.execute_input": "2026-09-03T23:22:40.580066Z", + "iopub.status.busy": "2026-09-03T23:22:40.579933Z", + "iopub.status.idle": "2026-09-03T23:22:40.804996Z", + "shell.execute_reply": "2026-09-03T23:22:40.804537Z" } }, "outputs": [ @@ -40,7 +53,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "status='healthy' service='zeroid' timestamp=datetime.datetime(2026, 9, 1, 4, 10, 53, 335986, tzinfo=TzInfo(0)) uptime_ms=6828 | run id: 348a22\n" + "status='healthy' service='zeroid' timestamp=datetime.datetime(2026, 9, 3, 23, 22, 40, 801620, tzinfo=TzInfo(0)) uptime_ms=56672 | run id: 1a5805\n" ] } ], @@ -58,7 +71,7 @@ }, { "cell_type": "markdown", - "id": "df509a15", + "id": "8f16d8c9", "metadata": {}, "source": [ "## 1 · Agent Registration Record — Layer 1 · The Passport (ODIS §6.1)\n", @@ -75,13 +88,13 @@ { "cell_type": "code", "execution_count": 2, - "id": "ccacbb77", + "id": "ee4e34ec", "metadata": { "execution": { - "iopub.execute_input": "2026-09-01T04:10:53.341024Z", - "iopub.status.busy": "2026-09-01T04:10:53.340914Z", - "iopub.status.idle": "2026-09-01T04:10:53.356109Z", - "shell.execute_reply": "2026-09-01T04:10:53.355623Z" + "iopub.execute_input": "2026-09-03T23:22:40.806269Z", + "iopub.status.busy": "2026-09-03T23:22:40.806170Z", + "iopub.status.idle": "2026-09-03T23:22:40.819669Z", + "shell.execute_reply": "2026-09-03T23:22:40.819222Z" } }, "outputs": [ @@ -89,7 +102,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-348a22 | trust: unverified | status: active\n" + "spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-1a5805 | trust: unverified | status: active\n" ] } ], @@ -103,7 +116,7 @@ }, { "cell_type": "markdown", - "id": "da8bb026", + "id": "4d6f979f", "metadata": {}, "source": [ "## 2 · Agent Runtime Credential — the Passport's output (ODIS §6.2, L1-05)\n", @@ -115,13 +128,13 @@ { "cell_type": "code", "execution_count": 3, - "id": "0979b715", + "id": "b5c740d4", "metadata": { "execution": { - "iopub.execute_input": "2026-09-01T04:10:53.357255Z", - "iopub.status.busy": "2026-09-01T04:10:53.357172Z", - "iopub.status.idle": "2026-09-01T04:10:53.365047Z", - "shell.execute_reply": "2026-09-01T04:10:53.364569Z" + "iopub.execute_input": "2026-09-03T23:22:40.820758Z", + "iopub.status.busy": "2026-09-03T23:22:40.820683Z", + "iopub.status.idle": "2026-09-03T23:22:40.827263Z", + "shell.execute_reply": "2026-09-03T23:22:40.826828Z" } }, "outputs": [ @@ -140,7 +153,7 @@ }, { "cell_type": "markdown", - "id": "b3052c00", + "id": "e949fd55", "metadata": {}, "source": [ "## 3 · The ODIS-aware target — the Bridge's native mode (ODIS §2.5 / L2-15)\n", @@ -149,12 +162,14 @@ "Runtime Credential and the active Delegation Record and enforces their **audience, holder\n", "binding, attenuation, constraints, freshness, and revocation** semantics. This section\n", "demonstrates the validation + attenuation/trust slice of that duty list; §5 covers\n", - "revocation. Audience enforcement is available (`tokens.verify(token, audience=...)`), and\n", - "holder-binding verification at the target (checking a DPoP proof against `cnf.jkt`) is what\n", - "ZeroID's Go `pkg/dpop` provides resource servers — not yet surfaced in the Python SDK.\n", + "revocation. Audience enforcement is demonstrated below; holder-binding verification at the\n", + "target (checking a DPoP proof against `cnf.jkt`) is what ZeroID's Go `pkg/dpop` provides\n", + "resource servers — not yet surfaced in the Python SDK.\n", "\n", - "`tokens.verify()` performs the local JWKS validation — no\n", - "round-trip to the authorization server — and the typed guards are the enforcement. Note the\n", + "`tokens.verify()` performs the local JWKS validation — no round-trip to the authorization\n", + "server. One caution before leaning on it: local verification is **revocation-blind** — a\n", + "token the server has already revoked keeps verifying until it expires. §5 demonstrates that\n", + "divergence and what a native-mode target must add. The typed guards are the enforcement. Note the\n", "trust gate fires at the *target*, complementing the issuance-side gate the raw notebook\n", "shows: the agent holds a perfectly valid token, and the tool server still refuses it until\n", "the identity is trusted enough." @@ -163,13 +178,13 @@ { "cell_type": "code", "execution_count": 4, - "id": "562b02ec", + "id": "cd048acb", "metadata": { "execution": { - "iopub.execute_input": "2026-09-01T04:10:53.366070Z", - "iopub.status.busy": "2026-09-01T04:10:53.365995Z", - "iopub.status.idle": "2026-09-01T04:10:53.372912Z", - "shell.execute_reply": "2026-09-01T04:10:53.372468Z" + "iopub.execute_input": "2026-09-03T23:22:40.828474Z", + "iopub.status.busy": "2026-09-03T23:22:40.828391Z", + "iopub.status.idle": "2026-09-03T23:22:40.835888Z", + "shell.execute_reply": "2026-09-03T23:22:40.835441Z" } }, "outputs": [ @@ -177,7 +192,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "verified: spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-348a22 | delegated: False\n", + "verified: spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-1a5805 | delegated: False\n", "refused (ZeroIDError): Missing required scope: 'admin:all'\n", "refused (ZeroIDError): Insufficient trust level: required 'first_party', got 'unverified'\n" ] @@ -198,24 +213,72 @@ }, { "cell_type": "markdown", - "id": "12ba8d36", + "id": "a2940696", "metadata": {}, "source": [ - "The identity gets promoted (here via the admin surface for brevity — the *attested*\n", - "promotion path, ODIS-L1-03/11, is demonstrated in the raw companion), and a fresh credential\n", - "now clears the target's trust gate:" + "Audience is the first duty on ODIS §2.5's native-mode list, and it is one argument\n", + "here: a target passes the audience it serves, and `verify` refuses tokens minted for anyone\n", + "else. (ZeroID stamps `aud` on every credential per JWT-SVID §3, defaulting to the issuer URL\n", + "when the requester names no audience.)" ] }, { "cell_type": "code", "execution_count": 5, - "id": "40ee32fa", + "id": "7349d817", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-03T23:22:40.837064Z", + "iopub.status.busy": "2026-09-03T23:22:40.836984Z", + "iopub.status.idle": "2026-09-03T23:22:40.839576Z", + "shell.execute_reply": "2026-09-03T23:22:40.839168Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "token aud: ['http://localhost:8899']\n", + "wrong audience refused (ZeroIDError): Token verification failed: Audience doesn't match\n" + ] + } + ], + "source": [ + "aud = jwt.decode(token.access_token, options={\"verify_signature\": False})[\"aud\"] # display-only peek\n", + "print(\"token aud:\", aud)\n", + "client.tokens.verify(token.access_token, audience=aud[0]) # expected audience: passes\n", + "try:\n", + " client.tokens.verify(token.access_token, audience=\"https://some-other-service.example\")\n", + "except Exception as e:\n", + " print(f\"wrong audience refused ({type(e).__name__}):\", e)" + ] + }, + { + "cell_type": "markdown", + "id": "ac2180dd", + "metadata": {}, + "source": [ + "Trust is **minted into the credential at issuance** — `trust_level` is a claim. So\n", + "promotion never upgrades outstanding tokens: the agent re-issues to benefit. The corollary\n", + "is the one that matters for security: *demotion doesn't downgrade them either* — a demoted\n", + "identity's already-issued credentials keep their elevated claim until expiry, which is why a\n", + "trust downgrade must travel with revocation (§5's compromise signal), not just a record\n", + "update. Here the identity gets promoted (via the admin surface for brevity — the *attested*\n", + "promotion path, ODIS-L1-03/11, is demonstrated in the raw companion), and a **fresh**\n", + "credential now clears the target's trust gate:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "98708fdd", "metadata": { "execution": { - "iopub.execute_input": "2026-09-01T04:10:53.374002Z", - "iopub.status.busy": "2026-09-01T04:10:53.373921Z", - "iopub.status.idle": "2026-09-01T04:10:53.382916Z", - "shell.execute_reply": "2026-09-01T04:10:53.382482Z" + "iopub.execute_input": "2026-09-03T23:22:40.840800Z", + "iopub.status.busy": "2026-09-03T23:22:40.840727Z", + "iopub.status.idle": "2026-09-03T23:22:40.853038Z", + "shell.execute_reply": "2026-09-03T23:22:40.852569Z" } }, "outputs": [ @@ -237,7 +300,7 @@ }, { "cell_type": "markdown", - "id": "fd4b5c21", + "id": "d3e57331", "metadata": {}, "source": [ "## 4 · Delegation Record — Layer 2 · The Bridge (ODIS §6.3, Pillar 4, L2-05/06)\n", @@ -254,14 +317,14 @@ }, { "cell_type": "code", - "execution_count": 6, - "id": "bb180871", + "execution_count": 7, + "id": "7bf28b51", "metadata": { "execution": { - "iopub.execute_input": "2026-09-01T04:10:53.384075Z", - "iopub.status.busy": "2026-09-01T04:10:53.383999Z", - "iopub.status.idle": "2026-09-01T04:10:53.397494Z", - "shell.execute_reply": "2026-09-01T04:10:53.397073Z" + "iopub.execute_input": "2026-09-03T23:22:40.854074Z", + "iopub.status.busy": "2026-09-03T23:22:40.853996Z", + "iopub.status.idle": "2026-09-03T23:22:40.866806Z", + "shell.execute_reply": "2026-09-03T23:22:40.866402Z" } }, "outputs": [ @@ -270,7 +333,7 @@ "output_type": "stream", "text": [ "granted scope (requested read+write): 'data:read'\n", - "sub: researcher-348a22 | is_delegated: True | act: {'sub': 'spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-348a22'} | depth: 1\n", + "sub: researcher-1a5805 | is_delegated: True | act: {'sub': 'spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-1a5805'} | depth: 1\n", "empty intersection refused (APIError): [400] invalid_scope: requested scopes are not available for delegation\n" ] } @@ -306,7 +369,7 @@ }, { "cell_type": "markdown", - "id": "27737340", + "id": "93fec9dd", "metadata": {}, "source": [ "## 5 · Compromise signal, cascade — and what native mode must add — L1-12, L3-04/05\n", @@ -322,14 +385,14 @@ }, { "cell_type": "code", - "execution_count": 7, - "id": "61a4fccf", + "execution_count": 8, + "id": "3546a818", "metadata": { "execution": { - "iopub.execute_input": "2026-09-01T04:10:53.398515Z", - "iopub.status.busy": "2026-09-01T04:10:53.398433Z", - "iopub.status.idle": "2026-09-01T04:10:53.404846Z", - "shell.execute_reply": "2026-09-01T04:10:53.404343Z" + "iopub.execute_input": "2026-09-03T23:22:40.867939Z", + "iopub.status.busy": "2026-09-03T23:22:40.867859Z", + "iopub.status.idle": "2026-09-03T23:22:40.874128Z", + "shell.execute_reply": "2026-09-03T23:22:40.873673Z" } }, "outputs": [ @@ -354,7 +417,7 @@ }, { "cell_type": "markdown", - "id": "9c25466e", + "id": "846d81a1", "metadata": {}, "source": [ "## What the SDK view added\n", @@ -362,6 +425,7 @@ "| ODIS concept | Where |\n", "|---|---|\n", "| Native-mode target validation (§2.5, L2-15) | §3 — local JWKS verify + typed guards |\n", + "| Audience enforcement (§2.5 duty list) | §3 — `verify(audience=...)` refusing a mis-audienced token |\n", "| Target-side trust gating (complement to issuance gating) | §3 — `require_trust` refusing a valid token |\n", "| Monotonic attenuation, both modes (L2-06) | §4 — silent narrowing + empty-intersection refusal |\n", "| Revocation semantics are part of native mode (L3-04, L2-11) | §5 — verify-vs-introspect divergence |\n", diff --git a/examples/odis/odis-walkthrough.ipynb b/examples/odis/odis-walkthrough.ipynb index e9371b27..b2166213 100644 --- a/examples/odis/odis-walkthrough.ipynb +++ b/examples/odis/odis-walkthrough.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "89e18c13", + "id": "803ebabb", "metadata": {}, "source": [ "# ZeroID as ODIS Layers 1–2 — an executable walkthrough\n", @@ -25,6 +25,15 @@ "pip install requests pyjwt cryptography\n", "```\n", "\n", + "> **Local dev trust model** — this compose deployment runs the admin plane (`/api/v1/*`)\n", + "> *unauthenticated*: tenancy comes from client-supplied `X-Account-ID` / `X-Project-ID`\n", + "> headers, and the CIBA approval in §7 names its subject in the request body. That is a\n", + "> deliberate dev-mode convenience so one notebook can play both the agent and the\n", + "> administrator. In production the admin plane sits behind an authenticated session, and\n", + "> tenancy and approving subject are derived from verified claims — never from the caller's\n", + "> say-so. The committed outputs were generated against the stock compose configuration,\n", + "> including `token.require_dpop: false` (see §5).\n", + "\n", "| ODIS term | In this notebook |\n", "|---|---|\n", "| Agent Registration Record (§6.1) | a ZeroID identity + its `CredentialPolicy` |\n", @@ -38,13 +47,13 @@ { "cell_type": "code", "execution_count": 1, - "id": "822cd8f8", + "id": "2dc4b870", "metadata": { "execution": { - "iopub.execute_input": "2026-09-01T04:10:51.838276Z", - "iopub.status.busy": "2026-09-01T04:10:51.838024Z", - "iopub.status.idle": "2026-09-01T04:10:51.943361Z", - "shell.execute_reply": "2026-09-01T04:10:51.942881Z" + "iopub.execute_input": "2026-09-03T23:22:39.087065Z", + "iopub.status.busy": "2026-09-03T23:22:39.086827Z", + "iopub.status.idle": "2026-09-03T23:22:39.383425Z", + "shell.execute_reply": "2026-09-03T23:22:39.382935Z" } }, "outputs": [ @@ -52,7 +61,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "zeroid: healthy | run id: 1066b3\n" + "zeroid: healthy | run id: 3eda01\n" ] } ], @@ -79,6 +88,8 @@ " jwk = {\"kty\": \"EC\", \"crv\": \"P-256\", \"x\": b64u(n.x.to_bytes(32, \"big\")), \"y\": b64u(n.y.to_bytes(32, \"big\"))}\n", " return k, pem, jwk\n", "\n", + "# Display-only decode: no signature check. Real targets verify against the JWKS\n", + "# (the SDK companion's §3 shows that path) — never trust unverified claims.\n", "def claims_of(tok): return jwt.decode(tok, options={\"verify_signature\": False})\n", "def show(obj): print(json.dumps(obj, indent=2, default=str))\n", "\n", @@ -88,7 +99,7 @@ }, { "cell_type": "markdown", - "id": "da1d7cfd", + "id": "d72fdb50", "metadata": {}, "source": [ "## 1 · Agent Registration Record — Layer 1 · The Passport (ODIS §6.1)\n", @@ -103,13 +114,13 @@ { "cell_type": "code", "execution_count": 2, - "id": "f06d5077", + "id": "669505f4", "metadata": { "execution": { - "iopub.execute_input": "2026-09-01T04:10:51.944596Z", - "iopub.status.busy": "2026-09-01T04:10:51.944482Z", - "iopub.status.idle": "2026-09-01T04:10:51.973726Z", - "shell.execute_reply": "2026-09-01T04:10:51.973251Z" + "iopub.execute_input": "2026-09-03T23:22:39.384778Z", + "iopub.status.busy": "2026-09-03T23:22:39.384649Z", + "iopub.status.idle": "2026-09-03T23:22:39.404026Z", + "shell.execute_reply": "2026-09-03T23:22:39.403528Z" } }, "outputs": [ @@ -118,7 +129,7 @@ "output_type": "stream", "text": [ "{\n", - " \"wimse_uri\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-1066b3\",\n", + " \"wimse_uri\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-3eda01\",\n", " \"identity_type\": \"agent\",\n", " \"trust_level\": \"unverified\",\n", " \"status\": \"active\",\n", @@ -142,7 +153,7 @@ }, { "cell_type": "markdown", - "id": "87c2fc37", + "id": "2067d78c", "metadata": {}, "source": [ "## 2 · Registration-record governance fields — Layer 1 · The Passport (ODIS §6.1)\n", @@ -158,13 +169,13 @@ { "cell_type": "code", "execution_count": 3, - "id": "4870ab26", + "id": "bb1fcd9c", "metadata": { "execution": { - "iopub.execute_input": "2026-09-01T04:10:51.974895Z", - "iopub.status.busy": "2026-09-01T04:10:51.974813Z", - "iopub.status.idle": "2026-09-01T04:10:51.986830Z", - "shell.execute_reply": "2026-09-01T04:10:51.986370Z" + "iopub.execute_input": "2026-09-03T23:22:39.405222Z", + "iopub.status.busy": "2026-09-03T23:22:39.405142Z", + "iopub.status.idle": "2026-09-03T23:22:39.417214Z", + "shell.execute_reply": "2026-09-03T23:22:39.416783Z" } }, "outputs": [ @@ -173,7 +184,7 @@ "output_type": "stream", "text": [ "{\n", - " \"name\": \"odis-demo-1066b3\",\n", + " \"name\": \"odis-demo-3eda01\",\n", " \"max_ttl_seconds\": 3600,\n", " \"required_trust_level\": \"first_party\",\n", " \"max_delegation_depth\": 2\n", @@ -197,7 +208,7 @@ }, { "cell_type": "markdown", - "id": "1574f8ca", + "id": "cbec431b", "metadata": {}, "source": [ "## 3 · No registration resolution, no authority — The Bridge refuses (ODIS-L2-14 + L1-11)\n", @@ -210,13 +221,13 @@ { "cell_type": "code", "execution_count": 4, - "id": "2ace86f3", + "id": "f23aea7a", "metadata": { "execution": { - "iopub.execute_input": "2026-09-01T04:10:51.988046Z", - "iopub.status.busy": "2026-09-01T04:10:51.987964Z", - "iopub.status.idle": "2026-09-01T04:10:51.996088Z", - "shell.execute_reply": "2026-09-01T04:10:51.995598Z" + "iopub.execute_input": "2026-09-03T23:22:39.418550Z", + "iopub.status.busy": "2026-09-03T23:22:39.418461Z", + "iopub.status.idle": "2026-09-03T23:22:39.426434Z", + "shell.execute_reply": "2026-09-03T23:22:39.426011Z" } }, "outputs": [ @@ -240,7 +251,7 @@ }, { "cell_type": "markdown", - "id": "695de017", + "id": "da42bff0", "metadata": {}, "source": [ "## 4 · Attestation raises trust — Layer 1 · The Passport (ODIS-L1-03 / L1-11)\n", @@ -257,13 +268,13 @@ { "cell_type": "code", "execution_count": 5, - "id": "f40299d7", + "id": "d96567f4", "metadata": { "execution": { - "iopub.execute_input": "2026-09-01T04:10:51.997157Z", - "iopub.status.busy": "2026-09-01T04:10:51.997082Z", - "iopub.status.idle": "2026-09-01T04:10:52.013593Z", - "shell.execute_reply": "2026-09-01T04:10:52.013142Z" + "iopub.execute_input": "2026-09-03T23:22:39.427529Z", + "iopub.status.busy": "2026-09-03T23:22:39.427448Z", + "iopub.status.idle": "2026-09-03T23:22:39.445452Z", + "shell.execute_reply": "2026-09-03T23:22:39.445040Z" } }, "outputs": [ @@ -288,7 +299,7 @@ }, { "cell_type": "markdown", - "id": "4908876e", + "id": "3e755b40", "metadata": {}, "source": [ "## 5 · Agent Runtime Credential — the Passport's output (ODIS §6.2, L1-05 / L1-09)\n", @@ -309,13 +320,13 @@ { "cell_type": "code", "execution_count": 6, - "id": "861d848b", + "id": "7f169d86", "metadata": { "execution": { - "iopub.execute_input": "2026-09-01T04:10:52.014749Z", - "iopub.status.busy": "2026-09-01T04:10:52.014679Z", - "iopub.status.idle": "2026-09-01T04:10:52.029918Z", - "shell.execute_reply": "2026-09-01T04:10:52.029457Z" + "iopub.execute_input": "2026-09-03T23:22:39.446563Z", + "iopub.status.busy": "2026-09-03T23:22:39.446481Z", + "iopub.status.idle": "2026-09-03T23:22:39.459330Z", + "shell.execute_reply": "2026-09-03T23:22:39.458851Z" } }, "outputs": [ @@ -325,12 +336,12 @@ "text": [ "token_type: DPoP | expires_in: 3600 s\n", "{\n", - " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-1066b3\",\n", + " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-3eda01\",\n", " \"trust_level\": \"first_party\",\n", - " \"mission_id\": \"e1c42e24-bf28-4430-b649-e747cde5523d\",\n", - " \"jti\": \"e1c42e24-bf28-4430-b649-e747cde5523d\"\n", + " \"mission_id\": \"bb887ec2-a37b-4fa3-8c16-2a1f06a1a320\",\n", + " \"jti\": \"bb887ec2-a37b-4fa3-8c16-2a1f06a1a320\"\n", "}\n", - "cnf: {'jkt': '1CQncC-hdKz54kXrbZMqS2G6nKKwB5vLHiaEpPE6Jn0'}\n" + "cnf: {'jkt': 'ysc8iRpwfyZEeIqh4sLz3q-Dw8DXmeIsZ1xf4ypq6Fs'}\n" ] } ], @@ -350,7 +361,7 @@ }, { "cell_type": "markdown", - "id": "2675b3a7", + "id": "d3ac0c74", "metadata": {}, "source": [ "A replayed proof must be rejected — the `jti` ledger is atomic (ODIS's replay concern\n", @@ -360,13 +371,13 @@ { "cell_type": "code", "execution_count": 7, - "id": "09970b7e", + "id": "053a159d", "metadata": { "execution": { - "iopub.execute_input": "2026-09-01T04:10:52.030944Z", - "iopub.status.busy": "2026-09-01T04:10:52.030868Z", - "iopub.status.idle": "2026-09-01T04:10:52.035382Z", - "shell.execute_reply": "2026-09-01T04:10:52.034966Z" + "iopub.execute_input": "2026-09-03T23:22:39.460403Z", + "iopub.status.busy": "2026-09-03T23:22:39.460318Z", + "iopub.status.idle": "2026-09-03T23:22:39.465625Z", + "shell.execute_reply": "2026-09-03T23:22:39.465170Z" } }, "outputs": [ @@ -391,7 +402,7 @@ }, { "cell_type": "markdown", - "id": "770f1636", + "id": "8250a675", "metadata": {}, "source": [ "## 6 · Delegation Record — Layer 2 · The Bridge (ODIS §6.3, Pillar 4, L2-01/05/06)\n", @@ -408,13 +419,13 @@ { "cell_type": "code", "execution_count": 8, - "id": "1bfed207", + "id": "dadaa445", "metadata": { "execution": { - "iopub.execute_input": "2026-09-01T04:10:52.036470Z", - "iopub.status.busy": "2026-09-01T04:10:52.036388Z", - "iopub.status.idle": "2026-09-01T04:10:52.055581Z", - "shell.execute_reply": "2026-09-01T04:10:52.055262Z" + "iopub.execute_input": "2026-09-03T23:22:39.466694Z", + "iopub.status.busy": "2026-09-03T23:22:39.466607Z", + "iopub.status.idle": "2026-09-03T23:22:39.485613Z", + "shell.execute_reply": "2026-09-03T23:22:39.485206Z" } }, "outputs": [ @@ -423,13 +434,13 @@ "output_type": "stream", "text": [ "{\n", - " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/researcher-1066b3\",\n", + " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/researcher-3eda01\",\n", " \"act\": {\n", - " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-1066b3\"\n", + " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-3eda01\"\n", " },\n", " \"delegation_depth\": 1,\n", - " \"mission_id\": \"e1c42e24-bf28-4430-b649-e747cde5523d\",\n", - " \"jti\": \"0ab9c9d8-c71a-4a43-972b-7c754b0e2e1c\"\n", + " \"mission_id\": \"bb887ec2-a37b-4fa3-8c16-2a1f06a1a320\",\n", + " \"jti\": \"5274f642-58d8-4517-8235-1590e08ec032\"\n", "}\n" ] } @@ -458,7 +469,7 @@ }, { "cell_type": "markdown", - "id": "93d3a37a", + "id": "7a8b9649", "metadata": {}, "source": [ "Attenuation is monotonic and fails closed: the researcher's ceiling is `data:read`,\n", @@ -470,13 +481,13 @@ { "cell_type": "code", "execution_count": 9, - "id": "e8dd1999", + "id": "cd333e1e", "metadata": { "execution": { - "iopub.execute_input": "2026-09-01T04:10:52.056777Z", - "iopub.status.busy": "2026-09-01T04:10:52.056701Z", - "iopub.status.idle": "2026-09-01T04:10:52.061831Z", - "shell.execute_reply": "2026-09-01T04:10:52.061389Z" + "iopub.execute_input": "2026-09-03T23:22:39.486662Z", + "iopub.status.busy": "2026-09-03T23:22:39.486586Z", + "iopub.status.idle": "2026-09-03T23:22:39.491738Z", + "shell.execute_reply": "2026-09-03T23:22:39.491273Z" } }, "outputs": [ @@ -502,7 +513,7 @@ }, { "cell_type": "markdown", - "id": "383d6788", + "id": "9bd291ba", "metadata": {}, "source": [ "## 7 · Bounded asynchronous human authorization — Layer 2 · The Bridge (ODIS-L2-02, Pillar 1)\n", @@ -513,19 +524,25 @@ "asynchronously (here via the admin surface; ping/push notifier modes exist), and only then\n", "does polling yield a token. The `binding_message` shows the human exactly what they are\n", "approving, and the issued token's `sub` is the **approving human** with the grant recorded\n", - "as `token_exchange: \"ciba\"` — originating-principal attribution, ODIS-CC-02." + "as `token_exchange: \"ciba\"` — originating-principal attribution, ODIS-CC-02.\n", + "\n", + "> The `/approve` call naming its subject in the request body is the dev-mode admin surface\n", + "> (see the trust-model note at the top); a production approval UI derives the subject from\n", + "> the authenticated session. And if the post-approval poll returns `access_denied` on an\n", + "> *upgraded* checkout, your stored default credential policy predates the CIBA grant —\n", + "> `docker compose down -v` for a fresh volume (see the README)." ] }, { "cell_type": "code", "execution_count": 10, - "id": "115c1a82", + "id": "c92794d7", "metadata": { "execution": { - "iopub.execute_input": "2026-09-01T04:10:52.062880Z", - "iopub.status.busy": "2026-09-01T04:10:52.062812Z", - "iopub.status.idle": "2026-09-01T04:10:52.379962Z", - "shell.execute_reply": "2026-09-01T04:10:52.378435Z" + "iopub.execute_input": "2026-09-03T23:22:39.492762Z", + "iopub.status.busy": "2026-09-03T23:22:39.492690Z", + "iopub.status.idle": "2026-09-03T23:22:39.805876Z", + "shell.execute_reply": "2026-09-03T23:22:39.805369Z" } }, "outputs": [ @@ -535,7 +552,7 @@ "text": [ "bc-authorize: 200\n", "{\n", - " \"auth_req_id\": \"5YcZ0SWxqb02oKzJi1dRTaHNnC4jOKyDJVt08J5bYF8\",\n", + " \"auth_req_id\": \"Tt0ijnQck9IF4IdeXEhNlZK9Cy7zRp-avS3RWVmfKVU\",\n", " \"expires_in\": 300,\n", " \"interval\": 5\n", "}\n" @@ -586,7 +603,7 @@ }, { "cell_type": "markdown", - "id": "f4e7dde9", + "id": "25b601cf", "metadata": {}, "source": [ "## 8 · Confirmed compromise → cascade revocation — ODIS-L1-12, L3-04 / L3-05\n", @@ -600,13 +617,13 @@ { "cell_type": "code", "execution_count": 11, - "id": "6d7f8cd0", + "id": "21073278", "metadata": { "execution": { - "iopub.execute_input": "2026-09-01T04:10:52.384050Z", - "iopub.status.busy": "2026-09-01T04:10:52.383562Z", - "iopub.status.idle": "2026-09-01T04:10:52.403032Z", - "shell.execute_reply": "2026-09-01T04:10:52.402133Z" + "iopub.execute_input": "2026-09-03T23:22:39.807263Z", + "iopub.status.busy": "2026-09-03T23:22:39.807146Z", + "iopub.status.idle": "2026-09-03T23:22:39.819883Z", + "shell.execute_reply": "2026-09-03T23:22:39.819408Z" } }, "outputs": [ @@ -614,7 +631,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "delegated token after cascade: {'active': False} (13 ms signal→dead)\n" + "delegated token after cascade: {'active': False} (10 ms signal→dead)\n" ] } ], @@ -630,7 +647,7 @@ }, { "cell_type": "markdown", - "id": "59362c1d", + "id": "672316f2", "metadata": {}, "source": [ "## 9 · Audit lineage survives the kill — ODIS-CC-01 / CC-02\n", @@ -643,13 +660,13 @@ { "cell_type": "code", "execution_count": 12, - "id": "349a6c07", + "id": "6caed8c2", "metadata": { "execution": { - "iopub.execute_input": "2026-09-01T04:10:52.406067Z", - "iopub.status.busy": "2026-09-01T04:10:52.405828Z", - "iopub.status.idle": "2026-09-01T04:10:52.416704Z", - "shell.execute_reply": "2026-09-01T04:10:52.415670Z" + "iopub.execute_input": "2026-09-03T23:22:39.821175Z", + "iopub.status.busy": "2026-09-03T23:22:39.821092Z", + "iopub.status.idle": "2026-09-03T23:22:39.827624Z", + "shell.execute_reply": "2026-09-03T23:22:39.827222Z" } }, "outputs": [ @@ -657,7 +674,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "chain: orch-1066b3 → researcher-1066b3\n", + "chain: orch-3eda01 → researcher-3eda01\n", "[\n", " {\n", " \"grant_type\": \"api_key\",\n", @@ -672,7 +689,7 @@ " ],\n", " \"attenuated\": [],\n", " \"is_revoked\": true,\n", - " \"revoke_reason\": \"auto-revoked by CAE signal fa2bd26d-2abe-4de9-b922-5f5075720674 (severity: critical)\"\n", + " \"revoke_reason\": \"auto-revoked by CAE signal fbb10756-71b2-44e7-a4f3-cfd2d4cf51e8 (severity: critical)\"\n", " },\n", " {\n", " \"grant_type\": \"token_exchange\",\n", @@ -688,7 +705,7 @@ " \"data:write\"\n", " ],\n", " \"is_revoked\": true,\n", - " \"revoke_reason\": \"auto-revoked by CAE signal fa2bd26d-2abe-4de9-b922-5f5075720674 (severity: critical)\"\n", + " \"revoke_reason\": \"auto-revoked by CAE signal fbb10756-71b2-44e7-a4f3-cfd2d4cf51e8 (severity: critical)\"\n", " }\n", "]\n" ] @@ -703,7 +720,7 @@ }, { "cell_type": "markdown", - "id": "6fe535aa", + "id": "8d875a62", "metadata": {}, "source": [ "## 10 · The sponsor leaves — offboarding kill switch (ODIS-L1-06 / L1-10 / L3-05)\n", @@ -712,19 +729,25 @@ "trigger de-provisioning. Alice — the human who just approved her proxy agent's authority in\n", "§7 — leaves the organization. One call deactivates every identity she owned and\n", "cascade-revokes their credentials, including the CIBA-approved token: the kill switch\n", - "(L3-05) keyed on the human, not the agent." + "(L3-05) keyed on the human, not the agent.\n", + "\n", + "Reading the response correctly: `credentials_revoked` is **not** the cascade count. The\n", + "cascade runs *inside* each identity's deactivation; this counter only reports stragglers\n", + "caught by a final safety-net sweep, so `0` is the healthy value. The proof of revocation is\n", + "the credential itself — introspection reports it dead, and the audit graph (§9) records the\n", + "edge as revoked with the offboarding reason:" ] }, { "cell_type": "code", "execution_count": 13, - "id": "a2e5852a", + "id": "056aa5e9", "metadata": { "execution": { - "iopub.execute_input": "2026-09-01T04:10:52.419903Z", - "iopub.status.busy": "2026-09-01T04:10:52.419704Z", - "iopub.status.idle": "2026-09-01T04:10:52.434367Z", - "shell.execute_reply": "2026-09-01T04:10:52.433819Z" + "iopub.execute_input": "2026-09-03T23:22:39.828904Z", + "iopub.status.busy": "2026-09-03T23:22:39.828826Z", + "iopub.status.idle": "2026-09-03T23:22:39.843953Z", + "shell.execute_reply": "2026-09-03T23:22:39.843475Z" } }, "outputs": [ @@ -737,20 +760,32 @@ " \"identities_deactivated\": 1,\n", " \"credentials_revoked\": 0\n", "}\n", - "alice's CIBA-approved token: {'active': False}\n" + "alice's CIBA-approved token: {'active': False}\n", + "[\n", + " {\n", + " \"grant_type\": \"urn:openid:params:grant-type:ciba\",\n", + " \"is_revoked\": true,\n", + " \"revoke_reason\": \"identity_deactivated\"\n", + " }\n", + "]\n" ] } ], "source": [ "r = api(\"POST\", \"/api/v1/identities/offboard-by-owner\", json={\"owner_user_id\": \"alice@example.com\"})\n", "show(r.json())\n", - "print(\"alice's CIBA-approved token:\",\n", - " requests.post(BASE + \"/oauth2/token/introspect\", json={\"token\": ciba_token[\"access_token\"]}).json())" + "dead = requests.post(BASE + \"/oauth2/token/introspect\", json={\"token\": ciba_token[\"access_token\"]}).json()\n", + "print(\"alice's CIBA-approved token:\", dead)\n", + "assert dead == {\"active\": False}\n", + "\n", + "g = api(\"GET\", f\"/api/v1/delegations/by-jti/{cc2['jti']}\").json()\n", + "show([{k: e[k] for k in (\"grant_type\", \"is_revoked\", \"revoke_reason\")} for e in g[\"edges\"]])\n", + "assert all(e[\"is_revoked\"] for e in g[\"edges\"])" ] }, { "cell_type": "markdown", - "id": "ab0d54b3", + "id": "8486c422", "metadata": {}, "source": [ "## What you just saw, in ODIS terms\n", From d0083f723214b50431819ad2e50636acccf5f462 Mon Sep 17 00:00:00 2001 From: Sharath Rajasekar Date: Thu, 3 Sep 2026 16:28:59 -0700 Subject: [PATCH 04/14] =?UTF-8?q?docs:=20explain=20the=20highflame=3D=3D0.?= =?UTF-8?q?3.17=20pin=20=E2=80=94=200.3.23=20targets=20the=20SaaS=20admin?= =?UTF-8?q?=20plane?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SDK notebook's version pin is load-bearing, not stale: from 0.3.23 the Python SDK serves the SaaS-shaped deployment (admin routes at the server root, nhi:manage bearer auth), while stock open-source zeroid mounts the admin API under /api/v1 (server.admin_path_prefix), so newer SDKs 404 on every admin call against the compose deployment. Documented in the README, the notebook prerequisites, and the SDK-gaps appendix (which now also notes 0.3.23's credential_policy_id= registration parameter). Co-Authored-By: Claude Fable 5 --- examples/odis/README.md | 7 +- examples/odis/odis-walkthrough-sdk.ipynb | 125 ++++++++++++----------- 2 files changed, 71 insertions(+), 61 deletions(-) diff --git a/examples/odis/README.md b/examples/odis/README.md index 1287e3e2..e29e410f 100644 --- a/examples/odis/README.md +++ b/examples/odis/README.md @@ -37,7 +37,12 @@ jupyter notebook examples/odis/odis-walkthrough.ipynb ``` The SDK notebook additionally needs `pip install "highflame==0.3.17"` — the -version its committed outputs were generated against. +version its committed outputs were generated against. The pin is deliberate, +not stale: from 0.3.23 the Python SDK targets the SaaS-shaped admin plane +(admin routes at the server root, authenticated with an `nhi:manage` bearer), +while stock open-source zeroid mounts the admin API under `/api/v1` +(`server.admin_path_prefix`), so newer SDKs 404 on every admin call against +this compose deployment. ## Which configuration each notebook runs under diff --git a/examples/odis/odis-walkthrough-sdk.ipynb b/examples/odis/odis-walkthrough-sdk.ipynb index 0d4e71b7..75269b78 100644 --- a/examples/odis/odis-walkthrough-sdk.ipynb +++ b/examples/odis/odis-walkthrough-sdk.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "e9f5a59e", + "id": "e97ae855", "metadata": {}, "source": [ "# ZeroID as ODIS Layers 1–2 — the SDK companion\n", @@ -21,7 +21,10 @@ "\n", "**Prerequisites** (repo root): `make setup-keys && docker compose up -d`, then\n", "`pip install \"highflame==0.3.17\" pyjwt cryptography` — 0.3.17 is the SDK version these\n", - "committed outputs were generated against.\n", + "committed outputs were generated against. The pin is deliberate: from 0.3.23 the SDK\n", + "targets the SaaS-shaped admin plane (admin routes at the server root, `nhi:manage`\n", + "bearer auth), while stock open-source zeroid mounts the admin API under `/api/v1`\n", + "(`server.admin_path_prefix`), so newer SDKs 404 on admin calls against this deployment.\n", "\n", "> **Configuration note (ODIS-L1-09):** this notebook requires the compose default\n", "> `token.require_dpop: false`. The Python SDK cannot construct DPoP proofs yet\n", @@ -39,13 +42,13 @@ { "cell_type": "code", "execution_count": 1, - "id": "59fad93b", + "id": "ef97a443", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:22:40.580066Z", - "iopub.status.busy": "2026-09-03T23:22:40.579933Z", - "iopub.status.idle": "2026-09-03T23:22:40.804996Z", - "shell.execute_reply": "2026-09-03T23:22:40.804537Z" + "iopub.execute_input": "2026-09-03T23:28:49.912524Z", + "iopub.status.busy": "2026-09-03T23:28:49.912323Z", + "iopub.status.idle": "2026-09-03T23:28:50.043380Z", + "shell.execute_reply": "2026-09-03T23:28:50.042907Z" } }, "outputs": [ @@ -53,7 +56,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "status='healthy' service='zeroid' timestamp=datetime.datetime(2026, 9, 3, 23, 22, 40, 801620, tzinfo=TzInfo(0)) uptime_ms=56672 | run id: 1a5805\n" + "status='healthy' service='zeroid' timestamp=datetime.datetime(2026, 9, 3, 23, 28, 50, 39547, tzinfo=TzInfo(0)) uptime_ms=425908 | run id: 3f36a6\n" ] } ], @@ -71,7 +74,7 @@ }, { "cell_type": "markdown", - "id": "8f16d8c9", + "id": "f0a25862", "metadata": {}, "source": [ "## 1 · Agent Registration Record — Layer 1 · The Passport (ODIS §6.1)\n", @@ -79,22 +82,23 @@ "`agents.register` creates the durable governance record and returns the bootstrap API key\n", "in one call. The agent is born `unverified` with a stable WIMSE URI (ODIS's `agent_id`).\n", "\n", - "> **SDK coverage note (0.3.17):** `client.credential_policies` can create issuance-ceiling\n", - "> policies, but there is no SDK parameter yet to *attach* one to an identity — the\n", - "> policy-gated fail-closed issuance demo therefore lives in the raw-HTTP companion. Worth\n", - "> an upstream SDK issue; the REST API supports it (`credential_policy_id`)." + "> **SDK coverage note:** in 0.3.17, `client.credential_policies` can create\n", + "> issuance-ceiling policies but there is no parameter to *attach* one — the policy-gated\n", + "> fail-closed issuance demo therefore lives in the raw-HTTP companion. (0.3.23 adds\n", + "> `credential_policy_id=` on `agents.register`, but targets the SaaS admin plane — see the\n", + "> pin note above.)" ] }, { "cell_type": "code", "execution_count": 2, - "id": "ee4e34ec", + "id": "47e4101f", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:22:40.806269Z", - "iopub.status.busy": "2026-09-03T23:22:40.806170Z", - "iopub.status.idle": "2026-09-03T23:22:40.819669Z", - "shell.execute_reply": "2026-09-03T23:22:40.819222Z" + "iopub.execute_input": "2026-09-03T23:28:50.044771Z", + "iopub.status.busy": "2026-09-03T23:28:50.044606Z", + "iopub.status.idle": "2026-09-03T23:28:50.070250Z", + "shell.execute_reply": "2026-09-03T23:28:50.069695Z" } }, "outputs": [ @@ -102,7 +106,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-1a5805 | trust: unverified | status: active\n" + "spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-3f36a6 | trust: unverified | status: active\n" ] } ], @@ -116,7 +120,7 @@ }, { "cell_type": "markdown", - "id": "4d6f979f", + "id": "cb181f04", "metadata": {}, "source": [ "## 2 · Agent Runtime Credential — the Passport's output (ODIS §6.2, L1-05)\n", @@ -128,13 +132,13 @@ { "cell_type": "code", "execution_count": 3, - "id": "b5c740d4", + "id": "d7933336", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:22:40.820758Z", - "iopub.status.busy": "2026-09-03T23:22:40.820683Z", - "iopub.status.idle": "2026-09-03T23:22:40.827263Z", - "shell.execute_reply": "2026-09-03T23:22:40.826828Z" + "iopub.execute_input": "2026-09-03T23:28:50.071369Z", + "iopub.status.busy": "2026-09-03T23:28:50.071288Z", + "iopub.status.idle": "2026-09-03T23:28:50.078975Z", + "shell.execute_reply": "2026-09-03T23:28:50.078440Z" } }, "outputs": [ @@ -153,7 +157,7 @@ }, { "cell_type": "markdown", - "id": "e949fd55", + "id": "3bc132d8", "metadata": {}, "source": [ "## 3 · The ODIS-aware target — the Bridge's native mode (ODIS §2.5 / L2-15)\n", @@ -178,13 +182,13 @@ { "cell_type": "code", "execution_count": 4, - "id": "cd048acb", + "id": "22ae5948", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:22:40.828474Z", - "iopub.status.busy": "2026-09-03T23:22:40.828391Z", - "iopub.status.idle": "2026-09-03T23:22:40.835888Z", - "shell.execute_reply": "2026-09-03T23:22:40.835441Z" + "iopub.execute_input": "2026-09-03T23:28:50.080016Z", + "iopub.status.busy": "2026-09-03T23:28:50.079939Z", + "iopub.status.idle": "2026-09-03T23:28:50.085990Z", + "shell.execute_reply": "2026-09-03T23:28:50.085572Z" } }, "outputs": [ @@ -192,7 +196,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "verified: spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-1a5805 | delegated: False\n", + "verified: spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-3f36a6 | delegated: False\n", "refused (ZeroIDError): Missing required scope: 'admin:all'\n", "refused (ZeroIDError): Insufficient trust level: required 'first_party', got 'unverified'\n" ] @@ -213,7 +217,7 @@ }, { "cell_type": "markdown", - "id": "a2940696", + "id": "19800041", "metadata": {}, "source": [ "Audience is the first duty on ODIS §2.5's native-mode list, and it is one argument\n", @@ -225,13 +229,13 @@ { "cell_type": "code", "execution_count": 5, - "id": "7349d817", + "id": "fc3adbed", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:22:40.837064Z", - "iopub.status.busy": "2026-09-03T23:22:40.836984Z", - "iopub.status.idle": "2026-09-03T23:22:40.839576Z", - "shell.execute_reply": "2026-09-03T23:22:40.839168Z" + "iopub.execute_input": "2026-09-03T23:28:50.087029Z", + "iopub.status.busy": "2026-09-03T23:28:50.086951Z", + "iopub.status.idle": "2026-09-03T23:28:50.089611Z", + "shell.execute_reply": "2026-09-03T23:28:50.089173Z" } }, "outputs": [ @@ -256,7 +260,7 @@ }, { "cell_type": "markdown", - "id": "ac2180dd", + "id": "3212d097", "metadata": {}, "source": [ "Trust is **minted into the credential at issuance** — `trust_level` is a claim. So\n", @@ -272,13 +276,13 @@ { "cell_type": "code", "execution_count": 6, - "id": "98708fdd", + "id": "f5079299", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:22:40.840800Z", - "iopub.status.busy": "2026-09-03T23:22:40.840727Z", - "iopub.status.idle": "2026-09-03T23:22:40.853038Z", - "shell.execute_reply": "2026-09-03T23:22:40.852569Z" + "iopub.execute_input": "2026-09-03T23:28:50.090671Z", + "iopub.status.busy": "2026-09-03T23:28:50.090592Z", + "iopub.status.idle": "2026-09-03T23:28:50.099915Z", + "shell.execute_reply": "2026-09-03T23:28:50.099433Z" } }, "outputs": [ @@ -300,7 +304,7 @@ }, { "cell_type": "markdown", - "id": "d3e57331", + "id": "a1f4296b", "metadata": {}, "source": [ "## 4 · Delegation Record — Layer 2 · The Bridge (ODIS §6.3, Pillar 4, L2-05/06)\n", @@ -318,13 +322,13 @@ { "cell_type": "code", "execution_count": 7, - "id": "7bf28b51", + "id": "47b7b366", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:22:40.854074Z", - "iopub.status.busy": "2026-09-03T23:22:40.853996Z", - "iopub.status.idle": "2026-09-03T23:22:40.866806Z", - "shell.execute_reply": "2026-09-03T23:22:40.866402Z" + "iopub.execute_input": "2026-09-03T23:28:50.100953Z", + "iopub.status.busy": "2026-09-03T23:28:50.100870Z", + "iopub.status.idle": "2026-09-03T23:28:50.114180Z", + "shell.execute_reply": "2026-09-03T23:28:50.113835Z" } }, "outputs": [ @@ -333,7 +337,7 @@ "output_type": "stream", "text": [ "granted scope (requested read+write): 'data:read'\n", - "sub: researcher-1a5805 | is_delegated: True | act: {'sub': 'spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-1a5805'} | depth: 1\n", + "sub: researcher-3f36a6 | is_delegated: True | act: {'sub': 'spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-3f36a6'} | depth: 1\n", "empty intersection refused (APIError): [400] invalid_scope: requested scopes are not available for delegation\n" ] } @@ -369,7 +373,7 @@ }, { "cell_type": "markdown", - "id": "93fec9dd", + "id": "c581a6c4", "metadata": {}, "source": [ "## 5 · Compromise signal, cascade — and what native mode must add — L1-12, L3-04/05\n", @@ -386,13 +390,13 @@ { "cell_type": "code", "execution_count": 8, - "id": "3546a818", + "id": "15ee9273", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:22:40.867939Z", - "iopub.status.busy": "2026-09-03T23:22:40.867859Z", - "iopub.status.idle": "2026-09-03T23:22:40.874128Z", - "shell.execute_reply": "2026-09-03T23:22:40.873673Z" + "iopub.execute_input": "2026-09-03T23:28:50.115320Z", + "iopub.status.busy": "2026-09-03T23:28:50.115236Z", + "iopub.status.idle": "2026-09-03T23:28:50.123393Z", + "shell.execute_reply": "2026-09-03T23:28:50.122916Z" } }, "outputs": [ @@ -417,7 +421,7 @@ }, { "cell_type": "markdown", - "id": "846d81a1", + "id": "184b9646", "metadata": {}, "source": [ "## What the SDK view added\n", @@ -431,8 +435,9 @@ "| Revocation semantics are part of native mode (L3-04, L2-11) | §5 — verify-vs-introspect divergence |\n", "\n", "**SDK gaps observed (0.3.17), worth upstream issues (tracked in sdk#105):** no\n", - "`credential_policy_id` attachment on identities/agents, no attestation submit/verify, no\n", - "delegation-graph reads, and no DPoP proof support on token issuance — now load-bearing,\n", + "`credential_policy_id` attachment on identities/agents (added in 0.3.23), no attestation\n", + "submit/verify, no delegation-graph reads, and no DPoP proof support on token\n", + "issuance — now load-bearing,\n", "since the server can refuse proof-less issuance deployment-wide (`token.require_dpop`,\n", "zeroid#304) — nor resource-server-side holder-binding (`cnf.jkt`/DPoP-proof) verification,\n", "so a Python native-mode target can enforce signature, expiry, scope, trust, delegation,\n", From 54a74e8cee769b759f2fc7d27dccc9d0eff2bdae Mon Sep 17 00:00:00 2001 From: Sharath Rajasekar Date: Thu, 3 Sep 2026 16:30:46 -0700 Subject: [PATCH 05/14] docs: keep the SDK version-pin rationale in the README only The notebook is customer-facing; the pin stays, the plumbing explanation (SaaS admin-plane shape from 0.3.23) lives in examples/odis/README.md. Co-Authored-By: Claude Fable 5 --- examples/odis/odis-walkthrough-sdk.ipynb | 124 +++++++++++------------ 1 file changed, 58 insertions(+), 66 deletions(-) diff --git a/examples/odis/odis-walkthrough-sdk.ipynb b/examples/odis/odis-walkthrough-sdk.ipynb index 75269b78..c4dbf2be 100644 --- a/examples/odis/odis-walkthrough-sdk.ipynb +++ b/examples/odis/odis-walkthrough-sdk.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "e97ae855", + "id": "285f589b", "metadata": {}, "source": [ "# ZeroID as ODIS Layers 1–2 — the SDK companion\n", @@ -20,11 +20,7 @@ " tool server calls before executing anything.\n", "\n", "**Prerequisites** (repo root): `make setup-keys && docker compose up -d`, then\n", - "`pip install \"highflame==0.3.17\" pyjwt cryptography` — 0.3.17 is the SDK version these\n", - "committed outputs were generated against. The pin is deliberate: from 0.3.23 the SDK\n", - "targets the SaaS-shaped admin plane (admin routes at the server root, `nhi:manage`\n", - "bearer auth), while stock open-source zeroid mounts the admin API under `/api/v1`\n", - "(`server.admin_path_prefix`), so newer SDKs 404 on admin calls against this deployment.\n", + "`pip install \"highflame==0.3.17\" pyjwt cryptography`.\n", "\n", "> **Configuration note (ODIS-L1-09):** this notebook requires the compose default\n", "> `token.require_dpop: false`. The Python SDK cannot construct DPoP proofs yet\n", @@ -42,13 +38,13 @@ { "cell_type": "code", "execution_count": 1, - "id": "ef97a443", + "id": "b94ec3a7", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:28:49.912524Z", - "iopub.status.busy": "2026-09-03T23:28:49.912323Z", - "iopub.status.idle": "2026-09-03T23:28:50.043380Z", - "shell.execute_reply": "2026-09-03T23:28:50.042907Z" + "iopub.execute_input": "2026-09-03T23:30:46.033675Z", + "iopub.status.busy": "2026-09-03T23:30:46.033391Z", + "iopub.status.idle": "2026-09-03T23:30:46.178966Z", + "shell.execute_reply": "2026-09-03T23:30:46.178468Z" } }, "outputs": [ @@ -56,7 +52,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "status='healthy' service='zeroid' timestamp=datetime.datetime(2026, 9, 3, 23, 28, 50, 39547, tzinfo=TzInfo(0)) uptime_ms=425908 | run id: 3f36a6\n" + "status='healthy' service='zeroid' timestamp=datetime.datetime(2026, 9, 3, 23, 30, 46, 175392, tzinfo=TzInfo(0)) uptime_ms=542043 | run id: 4d19f1\n" ] } ], @@ -74,7 +70,7 @@ }, { "cell_type": "markdown", - "id": "f0a25862", + "id": "97c8b894", "metadata": {}, "source": [ "## 1 · Agent Registration Record — Layer 1 · The Passport (ODIS §6.1)\n", @@ -82,23 +78,20 @@ "`agents.register` creates the durable governance record and returns the bootstrap API key\n", "in one call. The agent is born `unverified` with a stable WIMSE URI (ODIS's `agent_id`).\n", "\n", - "> **SDK coverage note:** in 0.3.17, `client.credential_policies` can create\n", - "> issuance-ceiling policies but there is no parameter to *attach* one — the policy-gated\n", - "> fail-closed issuance demo therefore lives in the raw-HTTP companion. (0.3.23 adds\n", - "> `credential_policy_id=` on `agents.register`, but targets the SaaS admin plane — see the\n", - "> pin note above.)" + "> **SDK coverage note:** the policy-gated fail-closed issuance demo lives in the raw-HTTP\n", + "> companion; this notebook stays on the target-side view." ] }, { "cell_type": "code", "execution_count": 2, - "id": "47e4101f", + "id": "2a6f6bf3", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:28:50.044771Z", - "iopub.status.busy": "2026-09-03T23:28:50.044606Z", - "iopub.status.idle": "2026-09-03T23:28:50.070250Z", - "shell.execute_reply": "2026-09-03T23:28:50.069695Z" + "iopub.execute_input": "2026-09-03T23:30:46.180309Z", + "iopub.status.busy": "2026-09-03T23:30:46.180208Z", + "iopub.status.idle": "2026-09-03T23:30:46.194444Z", + "shell.execute_reply": "2026-09-03T23:30:46.194016Z" } }, "outputs": [ @@ -106,7 +99,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-3f36a6 | trust: unverified | status: active\n" + "spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-4d19f1 | trust: unverified | status: active\n" ] } ], @@ -120,25 +113,25 @@ }, { "cell_type": "markdown", - "id": "cb181f04", + "id": "7368b5ea", "metadata": {}, "source": [ "## 2 · Agent Runtime Credential — the Passport's output (ODIS §6.2, L1-05)\n", "\n", "Short-lived, scoped issuance from the bootstrap key. (The DPoP holder-binding variant is in\n", - "the raw companion; the SDK issues bearer tokens as of 0.3.17.)" + "the raw companion; the SDK issues bearer tokens today.)" ] }, { "cell_type": "code", "execution_count": 3, - "id": "d7933336", + "id": "7c6a1cde", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:28:50.071369Z", - "iopub.status.busy": "2026-09-03T23:28:50.071288Z", - "iopub.status.idle": "2026-09-03T23:28:50.078975Z", - "shell.execute_reply": "2026-09-03T23:28:50.078440Z" + "iopub.execute_input": "2026-09-03T23:30:46.195688Z", + "iopub.status.busy": "2026-09-03T23:30:46.195600Z", + "iopub.status.idle": "2026-09-03T23:30:46.202225Z", + "shell.execute_reply": "2026-09-03T23:30:46.201811Z" } }, "outputs": [ @@ -157,7 +150,7 @@ }, { "cell_type": "markdown", - "id": "3bc132d8", + "id": "a78b887f", "metadata": {}, "source": [ "## 3 · The ODIS-aware target — the Bridge's native mode (ODIS §2.5 / L2-15)\n", @@ -182,13 +175,13 @@ { "cell_type": "code", "execution_count": 4, - "id": "22ae5948", + "id": "f9d079e6", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:28:50.080016Z", - "iopub.status.busy": "2026-09-03T23:28:50.079939Z", - "iopub.status.idle": "2026-09-03T23:28:50.085990Z", - "shell.execute_reply": "2026-09-03T23:28:50.085572Z" + "iopub.execute_input": "2026-09-03T23:30:46.203354Z", + "iopub.status.busy": "2026-09-03T23:30:46.203260Z", + "iopub.status.idle": "2026-09-03T23:30:46.209096Z", + "shell.execute_reply": "2026-09-03T23:30:46.208670Z" } }, "outputs": [ @@ -196,7 +189,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "verified: spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-3f36a6 | delegated: False\n", + "verified: spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-4d19f1 | delegated: False\n", "refused (ZeroIDError): Missing required scope: 'admin:all'\n", "refused (ZeroIDError): Insufficient trust level: required 'first_party', got 'unverified'\n" ] @@ -217,7 +210,7 @@ }, { "cell_type": "markdown", - "id": "19800041", + "id": "28a97e98", "metadata": {}, "source": [ "Audience is the first duty on ODIS §2.5's native-mode list, and it is one argument\n", @@ -229,13 +222,13 @@ { "cell_type": "code", "execution_count": 5, - "id": "fc3adbed", + "id": "3617bb32", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:28:50.087029Z", - "iopub.status.busy": "2026-09-03T23:28:50.086951Z", - "iopub.status.idle": "2026-09-03T23:28:50.089611Z", - "shell.execute_reply": "2026-09-03T23:28:50.089173Z" + "iopub.execute_input": "2026-09-03T23:30:46.210203Z", + "iopub.status.busy": "2026-09-03T23:30:46.210124Z", + "iopub.status.idle": "2026-09-03T23:30:46.212735Z", + "shell.execute_reply": "2026-09-03T23:30:46.212292Z" } }, "outputs": [ @@ -260,7 +253,7 @@ }, { "cell_type": "markdown", - "id": "3212d097", + "id": "496abca2", "metadata": {}, "source": [ "Trust is **minted into the credential at issuance** — `trust_level` is a claim. So\n", @@ -276,13 +269,13 @@ { "cell_type": "code", "execution_count": 6, - "id": "f5079299", + "id": "a8377bcb", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:28:50.090671Z", - "iopub.status.busy": "2026-09-03T23:28:50.090592Z", - "iopub.status.idle": "2026-09-03T23:28:50.099915Z", - "shell.execute_reply": "2026-09-03T23:28:50.099433Z" + "iopub.execute_input": "2026-09-03T23:30:46.213665Z", + "iopub.status.busy": "2026-09-03T23:30:46.213594Z", + "iopub.status.idle": "2026-09-03T23:30:46.224314Z", + "shell.execute_reply": "2026-09-03T23:30:46.223851Z" } }, "outputs": [ @@ -304,7 +297,7 @@ }, { "cell_type": "markdown", - "id": "a1f4296b", + "id": "7ed92005", "metadata": {}, "source": [ "## 4 · Delegation Record — Layer 2 · The Bridge (ODIS §6.3, Pillar 4, L2-05/06)\n", @@ -322,13 +315,13 @@ { "cell_type": "code", "execution_count": 7, - "id": "47b7b366", + "id": "dd8d2606", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:28:50.100953Z", - "iopub.status.busy": "2026-09-03T23:28:50.100870Z", - "iopub.status.idle": "2026-09-03T23:28:50.114180Z", - "shell.execute_reply": "2026-09-03T23:28:50.113835Z" + "iopub.execute_input": "2026-09-03T23:30:46.225289Z", + "iopub.status.busy": "2026-09-03T23:30:46.225205Z", + "iopub.status.idle": "2026-09-03T23:30:46.238799Z", + "shell.execute_reply": "2026-09-03T23:30:46.238316Z" } }, "outputs": [ @@ -337,7 +330,7 @@ "output_type": "stream", "text": [ "granted scope (requested read+write): 'data:read'\n", - "sub: researcher-3f36a6 | is_delegated: True | act: {'sub': 'spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-3f36a6'} | depth: 1\n", + "sub: researcher-4d19f1 | is_delegated: True | act: {'sub': 'spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-4d19f1'} | depth: 1\n", "empty intersection refused (APIError): [400] invalid_scope: requested scopes are not available for delegation\n" ] } @@ -373,7 +366,7 @@ }, { "cell_type": "markdown", - "id": "c581a6c4", + "id": "47335aa8", "metadata": {}, "source": [ "## 5 · Compromise signal, cascade — and what native mode must add — L1-12, L3-04/05\n", @@ -390,13 +383,13 @@ { "cell_type": "code", "execution_count": 8, - "id": "15ee9273", + "id": "8f5e29b9", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:28:50.115320Z", - "iopub.status.busy": "2026-09-03T23:28:50.115236Z", - "iopub.status.idle": "2026-09-03T23:28:50.123393Z", - "shell.execute_reply": "2026-09-03T23:28:50.122916Z" + "iopub.execute_input": "2026-09-03T23:30:46.239866Z", + "iopub.status.busy": "2026-09-03T23:30:46.239773Z", + "iopub.status.idle": "2026-09-03T23:30:46.246551Z", + "shell.execute_reply": "2026-09-03T23:30:46.246156Z" } }, "outputs": [ @@ -421,7 +414,7 @@ }, { "cell_type": "markdown", - "id": "184b9646", + "id": "fc391e56", "metadata": {}, "source": [ "## What the SDK view added\n", @@ -434,8 +427,7 @@ "| Monotonic attenuation, both modes (L2-06) | §4 — silent narrowing + empty-intersection refusal |\n", "| Revocation semantics are part of native mode (L3-04, L2-11) | §5 — verify-vs-introspect divergence |\n", "\n", - "**SDK gaps observed (0.3.17), worth upstream issues (tracked in sdk#105):** no\n", - "`credential_policy_id` attachment on identities/agents (added in 0.3.23), no attestation\n", + "**SDK gaps observed (0.3.17), tracked in sdk#105:** no attestation\n", "submit/verify, no delegation-graph reads, and no DPoP proof support on token\n", "issuance — now load-bearing,\n", "since the server can refuse proof-less issuance deployment-wide (`token.require_dpop`,\n", From 1b8030bf9c7370d5ceca6b13817f763516db6455 Mon Sep 17 00:00:00 2001 From: Sharath Rajasekar Date: Thu, 3 Sep 2026 16:34:22 -0700 Subject: [PATCH 06/14] docs: drop the SDK pin rationale from the README Co-Authored-By: Claude Fable 5 --- examples/odis/README.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/examples/odis/README.md b/examples/odis/README.md index e29e410f..1287e3e2 100644 --- a/examples/odis/README.md +++ b/examples/odis/README.md @@ -37,12 +37,7 @@ jupyter notebook examples/odis/odis-walkthrough.ipynb ``` The SDK notebook additionally needs `pip install "highflame==0.3.17"` — the -version its committed outputs were generated against. The pin is deliberate, -not stale: from 0.3.23 the Python SDK targets the SaaS-shaped admin plane -(admin routes at the server root, authenticated with an `nhi:manage` bearer), -while stock open-source zeroid mounts the admin API under `/api/v1` -(`server.admin_path_prefix`), so newer SDKs 404 on every admin call against -this compose deployment. +version its committed outputs were generated against. ## Which configuration each notebook runs under From 044a8cfe32a57f940dc6f8b078a47a0df340d12f Mon Sep 17 00:00:00 2001 From: Sharath Rajasekar Date: Thu, 3 Sep 2026 18:45:05 -0700 Subject: [PATCH 07/14] docs: migrate ODIS notebooks to the root-mounted admin API and SDK 0.3.23 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follows #318 (admin API at the router root by default; SDK pins at 0.3.23). The raw-HTTP walkthrough's admin calls drop the /api/v1 prefix, the SDK walkthrough pins highflame==0.3.23 (which speaks the root-mounted admin plane natively), and the statement re-pins its evidence commit to current main (d2531cd: #304's require_dpop/attestation/CIBA fixes plus #318's root mount). The L1-09 caveat stands — 0.3.23 still cannot construct DPoP proofs (sdk#105). Both notebooks re-executed against a fresh compose build of this branch; committed outputs real, secrets lint clean. Co-Authored-By: Claude Fable 5 --- docs/odis/role-capability-statement.md | 4 +- examples/odis/README.md | 4 +- examples/odis/odis-walkthrough-sdk.ipynb | 110 +++++------ examples/odis/odis-walkthrough.ipynb | 227 ++++++++++++----------- 4 files changed, 173 insertions(+), 172 deletions(-) diff --git a/docs/odis/role-capability-statement.md b/docs/odis/role-capability-statement.md index 168b6450..9e7dd423 100644 --- a/docs/odis/role-capability-statement.md +++ b/docs/odis/role-capability-statement.md @@ -2,7 +2,7 @@ **Status**: Draft for submission to the CoSAI/OASIS WS4 ODIS workstream **ODIS reference**: [ODIS.md draft](https://github.com/cosai-oasis/ws4-odis/blob/main/RFCs/ODIS.md) (unapproved contributor draft) -**ZeroID version**: commit `1676b4f` on `main` (PR #304 merged — `token.require_dpop`, attestation trust-clamp, CIBA credential anchoring; Apache-2.0, ) +**ZeroID version**: commit `d2531cd` on `main` (PR #304's `token.require_dpop`, attestation trust-clamp, and CIBA credential anchoring; PR #318's root-mounted admin API; Apache-2.0, ) **Claim type**: role-capability statement per ODIS §8. **This is not an ODIS Core, Extended, or Safety profile claim** — §8 reserves those for a complete conformance target, and this document itself identifies unmet MUSTs. ZeroID is an OAuth 2.1/OIDC authorization server for AI-agent identity, built @@ -129,7 +129,7 @@ style REV fill:#f0f0f0,color:#333,stroke:#999 | **ODIS-L1-06** Provisioning & De-provisioning | **Meets** | Lifecycle states `discovered→pending→active→suspended→deactivated/expired` (`domain/identity.go` `CanTransitionTo`); adopt/dismiss (`internal/handler/identity.go`); offboard-by-owner cascade (`internal/service/identity.go` `OffboardOwner`, DB `revoke_credentials_by_owner_cascade`); `tests/integration/discovery_lifecycle_test.go` | Satisfied via the "equivalent lifecycle-management mechanisms" clause — SCIM protocol endpoints themselves are not implemented. | | **ODIS-L1-07** Federated Trust *(Extended)* | **Meets** | Direct external-issuer OIDC federation: `external_issuers` config (`domain/external_issuer.go`), verification `internal/service/oauth_external_idp.go` (`subject_token_type=…:id_token`), upstream `iss` propagated as `user_id_iss`; `TestExternalIDTokenFederation_EndToEnd`, `_KeyRotation`, `_CrossTenantRejected` | Deployer-configured trust anchors; no automatic federation discovery. A trusted-service broker path also exists (`ExternalPrincipalExchange`) and is documented as the lossier fallback. | | **ODIS-L1-08** Trusted Distribution | **Gap** | — | No supply-chain verification before identity issuance. Pairs with L1-02. | -| **ODIS-L1-09** Holder-of-Key Authentication | **Meets (via configuration)** | DPoP with atomic jti replay ledger (`pkg/dpop/verifier.go`, `TestDPoPReplayRejected`); refresh tokens key-bound (`TestDPoPRefreshBoundWithDifferentKeyRejected`); binding propagates through delegation (`TestDPoPTokenExchange_PropagatesBindingToSubAgent`); WIMSE single-use nonce-bound proof tokens (`internal/service/proof.go`); **enforcement switch**: `token.require_dpop` refuses proof-less issuance with `invalid_dpop_proof` on every grant and advertises `dpop_bound_access_tokens_required` per RFC 9449 §5.1 (`TestRequireDPoPRefusesProoflessIssuance`, `TestRequireDPoPAdvertisedInASMetadata`) | The conformant posture is `token.require_dpop: true` (default off preserves Bearer opt-in): every issued credential is then proof-of-possession-bound. Client-side note: the Python SDK (0.3.17) does not yet construct DPoP proofs (tracked upstream, sdk#105), so a require_dpop deployment currently serves raw-HTTP/self-signing clients — the notebook demonstrates proof construction in a few lines of PyJWT. Consequently the SDK companion notebook runs under the default `require_dpop: false`; each notebook's required configuration is stated in `examples/odis/README.md`. | +| **ODIS-L1-09** Holder-of-Key Authentication | **Meets (via configuration)** | DPoP with atomic jti replay ledger (`pkg/dpop/verifier.go`, `TestDPoPReplayRejected`); refresh tokens key-bound (`TestDPoPRefreshBoundWithDifferentKeyRejected`); binding propagates through delegation (`TestDPoPTokenExchange_PropagatesBindingToSubAgent`); WIMSE single-use nonce-bound proof tokens (`internal/service/proof.go`); **enforcement switch**: `token.require_dpop` refuses proof-less issuance with `invalid_dpop_proof` on every grant and advertises `dpop_bound_access_tokens_required` per RFC 9449 §5.1 (`TestRequireDPoPRefusesProoflessIssuance`, `TestRequireDPoPAdvertisedInASMetadata`) | The conformant posture is `token.require_dpop: true` (default off preserves Bearer opt-in): every issued credential is then proof-of-possession-bound. Client-side note: the Python SDK (0.3.23) does not yet construct DPoP proofs (tracked upstream, sdk#105), so a require_dpop deployment currently serves raw-HTTP/self-signing clients — the notebook demonstrates proof construction in a few lines of PyJWT. Consequently the SDK companion notebook runs under the default `require_dpop: false`; each notebook's required configuration is stated in `examples/odis/README.md`. | | **ODIS-L1-10** Accountable Sponsor | **Meets** | `owner_user_id` verified against the tenant directory (CAP-DSC-004, `internal/service/identity.go`); offboard-by-owner cascade; ownerless as a surfaced posture signal | ODIS's administrative *drain* state is not implemented — lifecycle events revoke rather than drain. | | **ODIS-L1-11** Attestation-Bootstrapped Trust | **Meets (via configuration)** | Attestation raises `trust_level` exactly-once in-transaction (`internal/service/attestation.go` `trustLevelForAttestation`); `CredentialPolicy.RequiredTrustLevel`/`RequiredAttestation` gate issuance (`EnforcePolicy` checks 4–5); expiry demotes (`TestExpiredAttestationNoLongerSatisfiesPolicy`) | The conformant posture: `RequiredTrustLevel`/`RequiredAttestation` on the identity's `CredentialPolicy` makes issuance depend on verified attestation, fail-closed (demonstrated live in the companion notebook, §3–§4). Tenants that omit the requirement have chosen a non-ODIS posture; the enforcement machinery is shipped and tested. | | **ODIS-L1-12** Runtime Security State | **Meets** | Signal ingest `POST /signals/ingest` (`internal/handler/signal.go`); severity-driven revocation with cascade (`TestCAECriticalSignalRevokesCredential`, `TestCAESignalCascadesRevocationToChildren`) | Signal schema is ZeroID-local, not CAEP event-type URIs (see `COMPLIANCE.md`'s honest SSF/CAEP row). | diff --git a/examples/odis/README.md b/examples/odis/README.md index 1287e3e2..0e571a63 100644 --- a/examples/odis/README.md +++ b/examples/odis/README.md @@ -36,7 +36,7 @@ pip install requests pyjwt cryptography jupyter jupyter notebook examples/odis/odis-walkthrough.ipynb ``` -The SDK notebook additionally needs `pip install "highflame==0.3.17"` — the +The SDK notebook additionally needs `pip install "highflame==0.3.23"` — the version its committed outputs were generated against. ## Which configuration each notebook runs under @@ -47,7 +47,7 @@ Both notebooks execute against the stock compose deployment — in particular - The raw-HTTP walkthrough **chooses** the default so §5 can show the Bearer fallback and the configuration switch that closes it; every DPoP behavior (holder binding, replay rejection) is still demonstrated live. -- The SDK walkthrough **requires** the default: the Python SDK (0.3.17) cannot +- The SDK walkthrough **requires** the default: the Python SDK (0.3.23) cannot construct DPoP proofs yet (highflame-sdk#105), so under `require_dpop: true` — the hardened posture the role-capability statement grades L1-09 against — its issuance calls are refused with `invalid_dpop_proof`. diff --git a/examples/odis/odis-walkthrough-sdk.ipynb b/examples/odis/odis-walkthrough-sdk.ipynb index c4dbf2be..c2ca7347 100644 --- a/examples/odis/odis-walkthrough-sdk.ipynb +++ b/examples/odis/odis-walkthrough-sdk.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "285f589b", + "id": "749b8cd0", "metadata": {}, "source": [ "# ZeroID as ODIS Layers 1–2 — the SDK companion\n", @@ -20,7 +20,7 @@ " tool server calls before executing anything.\n", "\n", "**Prerequisites** (repo root): `make setup-keys && docker compose up -d`, then\n", - "`pip install \"highflame==0.3.17\" pyjwt cryptography`.\n", + "`pip install \"highflame==0.3.23\" pyjwt cryptography`.\n", "\n", "> **Configuration note (ODIS-L1-09):** this notebook requires the compose default\n", "> `token.require_dpop: false`. The Python SDK cannot construct DPoP proofs yet\n", @@ -38,13 +38,13 @@ { "cell_type": "code", "execution_count": 1, - "id": "b94ec3a7", + "id": "a70bb62b", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:30:46.033675Z", - "iopub.status.busy": "2026-09-03T23:30:46.033391Z", - "iopub.status.idle": "2026-09-03T23:30:46.178966Z", - "shell.execute_reply": "2026-09-03T23:30:46.178468Z" + "iopub.execute_input": "2026-09-04T01:44:45.277918Z", + "iopub.status.busy": "2026-09-04T01:44:45.277673Z", + "iopub.status.idle": "2026-09-04T01:44:45.423450Z", + "shell.execute_reply": "2026-09-04T01:44:45.423045Z" } }, "outputs": [ @@ -52,7 +52,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "status='healthy' service='zeroid' timestamp=datetime.datetime(2026, 9, 3, 23, 30, 46, 175392, tzinfo=TzInfo(0)) uptime_ms=542043 | run id: 4d19f1\n" + "status='healthy' service='zeroid' timestamp=datetime.datetime(2026, 9, 4, 1, 44, 45, 413077, tzinfo=TzInfo(0)) uptime_ms=12792 | run id: 5a2d4a\n" ] } ], @@ -70,7 +70,7 @@ }, { "cell_type": "markdown", - "id": "97c8b894", + "id": "2113649a", "metadata": {}, "source": [ "## 1 · Agent Registration Record — Layer 1 · The Passport (ODIS §6.1)\n", @@ -85,13 +85,13 @@ { "cell_type": "code", "execution_count": 2, - "id": "2a6f6bf3", + "id": "34514dca", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:30:46.180309Z", - "iopub.status.busy": "2026-09-03T23:30:46.180208Z", - "iopub.status.idle": "2026-09-03T23:30:46.194444Z", - "shell.execute_reply": "2026-09-03T23:30:46.194016Z" + "iopub.execute_input": "2026-09-04T01:44:45.424626Z", + "iopub.status.busy": "2026-09-04T01:44:45.424524Z", + "iopub.status.idle": "2026-09-04T01:44:45.437524Z", + "shell.execute_reply": "2026-09-04T01:44:45.437080Z" } }, "outputs": [ @@ -99,7 +99,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-4d19f1 | trust: unverified | status: active\n" + "spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-5a2d4a | trust: unverified | status: active\n" ] } ], @@ -113,7 +113,7 @@ }, { "cell_type": "markdown", - "id": "7368b5ea", + "id": "b530138c", "metadata": {}, "source": [ "## 2 · Agent Runtime Credential — the Passport's output (ODIS §6.2, L1-05)\n", @@ -125,13 +125,13 @@ { "cell_type": "code", "execution_count": 3, - "id": "7c6a1cde", + "id": "36b989f9", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:30:46.195688Z", - "iopub.status.busy": "2026-09-03T23:30:46.195600Z", - "iopub.status.idle": "2026-09-03T23:30:46.202225Z", - "shell.execute_reply": "2026-09-03T23:30:46.201811Z" + "iopub.execute_input": "2026-09-04T01:44:45.438597Z", + "iopub.status.busy": "2026-09-04T01:44:45.438525Z", + "iopub.status.idle": "2026-09-04T01:44:45.447689Z", + "shell.execute_reply": "2026-09-04T01:44:45.447249Z" } }, "outputs": [ @@ -150,7 +150,7 @@ }, { "cell_type": "markdown", - "id": "a78b887f", + "id": "5d8a4618", "metadata": {}, "source": [ "## 3 · The ODIS-aware target — the Bridge's native mode (ODIS §2.5 / L2-15)\n", @@ -175,13 +175,13 @@ { "cell_type": "code", "execution_count": 4, - "id": "f9d079e6", + "id": "a59bdc93", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:30:46.203354Z", - "iopub.status.busy": "2026-09-03T23:30:46.203260Z", - "iopub.status.idle": "2026-09-03T23:30:46.209096Z", - "shell.execute_reply": "2026-09-03T23:30:46.208670Z" + "iopub.execute_input": "2026-09-04T01:44:45.448867Z", + "iopub.status.busy": "2026-09-04T01:44:45.448777Z", + "iopub.status.idle": "2026-09-04T01:44:45.456363Z", + "shell.execute_reply": "2026-09-04T01:44:45.455915Z" } }, "outputs": [ @@ -189,7 +189,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "verified: spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-4d19f1 | delegated: False\n", + "verified: spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-5a2d4a | delegated: False\n", "refused (ZeroIDError): Missing required scope: 'admin:all'\n", "refused (ZeroIDError): Insufficient trust level: required 'first_party', got 'unverified'\n" ] @@ -210,7 +210,7 @@ }, { "cell_type": "markdown", - "id": "28a97e98", + "id": "57d8b38f", "metadata": {}, "source": [ "Audience is the first duty on ODIS §2.5's native-mode list, and it is one argument\n", @@ -222,13 +222,13 @@ { "cell_type": "code", "execution_count": 5, - "id": "3617bb32", + "id": "d5029aea", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:30:46.210203Z", - "iopub.status.busy": "2026-09-03T23:30:46.210124Z", - "iopub.status.idle": "2026-09-03T23:30:46.212735Z", - "shell.execute_reply": "2026-09-03T23:30:46.212292Z" + "iopub.execute_input": "2026-09-04T01:44:45.457398Z", + "iopub.status.busy": "2026-09-04T01:44:45.457320Z", + "iopub.status.idle": "2026-09-04T01:44:45.459927Z", + "shell.execute_reply": "2026-09-04T01:44:45.459532Z" } }, "outputs": [ @@ -253,7 +253,7 @@ }, { "cell_type": "markdown", - "id": "496abca2", + "id": "76d6841e", "metadata": {}, "source": [ "Trust is **minted into the credential at issuance** — `trust_level` is a claim. So\n", @@ -269,13 +269,13 @@ { "cell_type": "code", "execution_count": 6, - "id": "a8377bcb", + "id": "77b16706", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:30:46.213665Z", - "iopub.status.busy": "2026-09-03T23:30:46.213594Z", - "iopub.status.idle": "2026-09-03T23:30:46.224314Z", - "shell.execute_reply": "2026-09-03T23:30:46.223851Z" + "iopub.execute_input": "2026-09-04T01:44:45.460965Z", + "iopub.status.busy": "2026-09-04T01:44:45.460869Z", + "iopub.status.idle": "2026-09-04T01:44:45.470154Z", + "shell.execute_reply": "2026-09-04T01:44:45.469735Z" } }, "outputs": [ @@ -297,7 +297,7 @@ }, { "cell_type": "markdown", - "id": "7ed92005", + "id": "9d56aaeb", "metadata": {}, "source": [ "## 4 · Delegation Record — Layer 2 · The Bridge (ODIS §6.3, Pillar 4, L2-05/06)\n", @@ -315,13 +315,13 @@ { "cell_type": "code", "execution_count": 7, - "id": "dd8d2606", + "id": "a55093bf", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:30:46.225289Z", - "iopub.status.busy": "2026-09-03T23:30:46.225205Z", - "iopub.status.idle": "2026-09-03T23:30:46.238799Z", - "shell.execute_reply": "2026-09-03T23:30:46.238316Z" + "iopub.execute_input": "2026-09-04T01:44:45.471144Z", + "iopub.status.busy": "2026-09-04T01:44:45.471072Z", + "iopub.status.idle": "2026-09-04T01:44:45.484638Z", + "shell.execute_reply": "2026-09-04T01:44:45.484205Z" } }, "outputs": [ @@ -330,7 +330,7 @@ "output_type": "stream", "text": [ "granted scope (requested read+write): 'data:read'\n", - "sub: researcher-4d19f1 | is_delegated: True | act: {'sub': 'spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-4d19f1'} | depth: 1\n", + "sub: researcher-5a2d4a | is_delegated: True | act: {'sub': 'spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-5a2d4a'} | depth: 1\n", "empty intersection refused (APIError): [400] invalid_scope: requested scopes are not available for delegation\n" ] } @@ -366,7 +366,7 @@ }, { "cell_type": "markdown", - "id": "47335aa8", + "id": "f8becf10", "metadata": {}, "source": [ "## 5 · Compromise signal, cascade — and what native mode must add — L1-12, L3-04/05\n", @@ -383,13 +383,13 @@ { "cell_type": "code", "execution_count": 8, - "id": "8f5e29b9", + "id": "7c5b1f82", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:30:46.239866Z", - "iopub.status.busy": "2026-09-03T23:30:46.239773Z", - "iopub.status.idle": "2026-09-03T23:30:46.246551Z", - "shell.execute_reply": "2026-09-03T23:30:46.246156Z" + "iopub.execute_input": "2026-09-04T01:44:45.485807Z", + "iopub.status.busy": "2026-09-04T01:44:45.485720Z", + "iopub.status.idle": "2026-09-04T01:44:45.492259Z", + "shell.execute_reply": "2026-09-04T01:44:45.491804Z" } }, "outputs": [ @@ -414,7 +414,7 @@ }, { "cell_type": "markdown", - "id": "fc391e56", + "id": "aae7dab2", "metadata": {}, "source": [ "## What the SDK view added\n", @@ -427,7 +427,7 @@ "| Monotonic attenuation, both modes (L2-06) | §4 — silent narrowing + empty-intersection refusal |\n", "| Revocation semantics are part of native mode (L3-04, L2-11) | §5 — verify-vs-introspect divergence |\n", "\n", - "**SDK gaps observed (0.3.17), tracked in sdk#105:** no attestation\n", + "**SDK gaps observed (0.3.23), tracked in sdk#105:** no attestation\n", "submit/verify, no delegation-graph reads, and no DPoP proof support on token\n", "issuance — now load-bearing,\n", "since the server can refuse proof-less issuance deployment-wide (`token.require_dpop`,\n", diff --git a/examples/odis/odis-walkthrough.ipynb b/examples/odis/odis-walkthrough.ipynb index b2166213..9a84121c 100644 --- a/examples/odis/odis-walkthrough.ipynb +++ b/examples/odis/odis-walkthrough.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "803ebabb", + "id": "a5dfad0e", "metadata": {}, "source": [ "# ZeroID as ODIS Layers 1–2 — an executable walkthrough\n", @@ -25,7 +25,8 @@ "pip install requests pyjwt cryptography\n", "```\n", "\n", - "> **Local dev trust model** — this compose deployment runs the admin plane (`/api/v1/*`)\n", + "> **Local dev trust model** — this compose deployment runs the admin plane (identities,\n", + "> agents, policies, signals — served at the server root, zeroid#318)\n", "> *unauthenticated*: tenancy comes from client-supplied `X-Account-ID` / `X-Project-ID`\n", "> headers, and the CIBA approval in §7 names its subject in the request body. That is a\n", "> deliberate dev-mode convenience so one notebook can play both the agent and the\n", @@ -47,13 +48,13 @@ { "cell_type": "code", "execution_count": 1, - "id": "2dc4b870", + "id": "b8619930", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:22:39.087065Z", - "iopub.status.busy": "2026-09-03T23:22:39.086827Z", - "iopub.status.idle": "2026-09-03T23:22:39.383425Z", - "shell.execute_reply": "2026-09-03T23:22:39.382935Z" + "iopub.execute_input": "2026-09-04T01:44:43.938778Z", + "iopub.status.busy": "2026-09-04T01:44:43.938631Z", + "iopub.status.idle": "2026-09-04T01:44:44.045024Z", + "shell.execute_reply": "2026-09-04T01:44:44.044538Z" } }, "outputs": [ @@ -61,7 +62,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "zeroid: healthy | run id: 3eda01\n" + "zeroid: healthy | run id: e54982\n" ] } ], @@ -99,7 +100,7 @@ }, { "cell_type": "markdown", - "id": "d72fdb50", + "id": "895cea1c", "metadata": {}, "source": [ "## 1 · Agent Registration Record — Layer 1 · The Passport (ODIS §6.1)\n", @@ -114,13 +115,13 @@ { "cell_type": "code", "execution_count": 2, - "id": "669505f4", + "id": "8a1ed311", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:22:39.384778Z", - "iopub.status.busy": "2026-09-03T23:22:39.384649Z", - "iopub.status.idle": "2026-09-03T23:22:39.404026Z", - "shell.execute_reply": "2026-09-03T23:22:39.403528Z" + "iopub.execute_input": "2026-09-04T01:44:44.046304Z", + "iopub.status.busy": "2026-09-04T01:44:44.046178Z", + "iopub.status.idle": "2026-09-04T01:44:44.079582Z", + "shell.execute_reply": "2026-09-04T01:44:44.079089Z" } }, "outputs": [ @@ -129,7 +130,7 @@ "output_type": "stream", "text": [ "{\n", - " \"wimse_uri\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-3eda01\",\n", + " \"wimse_uri\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-e54982\",\n", " \"identity_type\": \"agent\",\n", " \"trust_level\": \"unverified\",\n", " \"status\": \"active\",\n", @@ -140,7 +141,7 @@ ], "source": [ "orch_key, orch_pem, orch_jwk = gen_key()\n", - "r = api(\"POST\", \"/api/v1/agents/register\", json={\n", + "r = api(\"POST\", \"/agents/register\", json={\n", " \"name\": \"Orchestrator\", \"external_id\": f\"orch-{run}\",\n", " \"identity_type\": \"agent\", \"sub_type\": \"orchestrator\",\n", " \"allowed_scopes\": [\"data:read\", \"data:write\"],\n", @@ -153,7 +154,7 @@ }, { "cell_type": "markdown", - "id": "2067d78c", + "id": "9d85eaed", "metadata": {}, "source": [ "## 2 · Registration-record governance fields — Layer 1 · The Passport (ODIS §6.1)\n", @@ -169,13 +170,13 @@ { "cell_type": "code", "execution_count": 3, - "id": "bb1fcd9c", + "id": "e30002b6", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:22:39.405222Z", - "iopub.status.busy": "2026-09-03T23:22:39.405142Z", - "iopub.status.idle": "2026-09-03T23:22:39.417214Z", - "shell.execute_reply": "2026-09-03T23:22:39.416783Z" + "iopub.execute_input": "2026-09-04T01:44:44.080661Z", + "iopub.status.busy": "2026-09-04T01:44:44.080584Z", + "iopub.status.idle": "2026-09-04T01:44:44.091892Z", + "shell.execute_reply": "2026-09-04T01:44:44.091419Z" } }, "outputs": [ @@ -184,7 +185,7 @@ "output_type": "stream", "text": [ "{\n", - " \"name\": \"odis-demo-3eda01\",\n", + " \"name\": \"odis-demo-e54982\",\n", " \"max_ttl_seconds\": 3600,\n", " \"required_trust_level\": \"first_party\",\n", " \"max_delegation_depth\": 2\n", @@ -193,7 +194,7 @@ } ], "source": [ - "r = api(\"POST\", \"/api/v1/credential-policies\", json={\n", + "r = api(\"POST\", \"/credential-policies\", json={\n", " \"name\": f\"odis-demo-{run}\",\n", " \"max_ttl_seconds\": 3600, # L1-05: bounded lifetime\n", " \"allowed_grant_types\": [\"api_key\", \"token_exchange\", \"client_credentials\"],\n", @@ -202,13 +203,13 @@ " \"max_delegation_depth\": 2, # Pillar 4: bounded cascade\n", "})\n", "policy = r.json()\n", - "api(\"PATCH\", f\"/api/v1/identities/{orch_id}\", json={\"credential_policy_id\": policy[\"id\"]})\n", + "api(\"PATCH\", f\"/identities/{orch_id}\", json={\"credential_policy_id\": policy[\"id\"]})\n", "show({k: policy[k] for k in (\"name\", \"max_ttl_seconds\", \"required_trust_level\", \"max_delegation_depth\")})" ] }, { "cell_type": "markdown", - "id": "cbec431b", + "id": "4fe6c333", "metadata": {}, "source": [ "## 3 · No registration resolution, no authority — The Bridge refuses (ODIS-L2-14 + L1-11)\n", @@ -221,13 +222,13 @@ { "cell_type": "code", "execution_count": 4, - "id": "f23aea7a", + "id": "acbd64a2", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:22:39.418550Z", - "iopub.status.busy": "2026-09-03T23:22:39.418461Z", - "iopub.status.idle": "2026-09-03T23:22:39.426434Z", - "shell.execute_reply": "2026-09-03T23:22:39.426011Z" + "iopub.execute_input": "2026-09-04T01:44:44.092984Z", + "iopub.status.busy": "2026-09-04T01:44:44.092903Z", + "iopub.status.idle": "2026-09-04T01:44:44.101174Z", + "shell.execute_reply": "2026-09-04T01:44:44.100632Z" } }, "outputs": [ @@ -251,7 +252,7 @@ }, { "cell_type": "markdown", - "id": "da42bff0", + "id": "ced783ad", "metadata": {}, "source": [ "## 4 · Attestation raises trust — Layer 1 · The Passport (ODIS-L1-03 / L1-11)\n", @@ -268,13 +269,13 @@ { "cell_type": "code", "execution_count": 5, - "id": "d96567f4", + "id": "5cc1557d", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:22:39.427529Z", - "iopub.status.busy": "2026-09-03T23:22:39.427448Z", - "iopub.status.idle": "2026-09-03T23:22:39.445452Z", - "shell.execute_reply": "2026-09-03T23:22:39.445040Z" + "iopub.execute_input": "2026-09-04T01:44:44.102394Z", + "iopub.status.busy": "2026-09-04T01:44:44.102316Z", + "iopub.status.idle": "2026-09-04T01:44:44.121071Z", + "shell.execute_reply": "2026-09-04T01:44:44.120581Z" } }, "outputs": [ @@ -288,18 +289,18 @@ } ], "source": [ - "att = api(\"POST\", \"/api/v1/attestation/submit\", json={\n", + "att = api(\"POST\", \"/attestation/submit\", json={\n", " \"identity_id\": orch_id, \"level\": \"hardware\",\n", " \"proof_type\": \"image_hash\", \"proof_value\": \"sha256:deadbeef\", # dev stub: local demo only\n", "}).json()\n", - "r = api(\"POST\", \"/api/v1/attestation/verify\", json={\"attestation_id\": att[\"id\"]})\n", + "r = api(\"POST\", \"/attestation/verify\", json={\"attestation_id\": att[\"id\"]})\n", "print(\"verify:\", r.status_code)\n", - "print(\"trust level now:\", api(\"GET\", f\"/api/v1/identities/{orch_id}\").json()[\"trust_level\"])" + "print(\"trust level now:\", api(\"GET\", f\"/identities/{orch_id}\").json()[\"trust_level\"])" ] }, { "cell_type": "markdown", - "id": "3e755b40", + "id": "a7429310", "metadata": {}, "source": [ "## 5 · Agent Runtime Credential — the Passport's output (ODIS §6.2, L1-05 / L1-09)\n", @@ -320,13 +321,13 @@ { "cell_type": "code", "execution_count": 6, - "id": "7f169d86", + "id": "ff51a6d5", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:22:39.446563Z", - "iopub.status.busy": "2026-09-03T23:22:39.446481Z", - "iopub.status.idle": "2026-09-03T23:22:39.459330Z", - "shell.execute_reply": "2026-09-03T23:22:39.458851Z" + "iopub.execute_input": "2026-09-04T01:44:44.122211Z", + "iopub.status.busy": "2026-09-04T01:44:44.122126Z", + "iopub.status.idle": "2026-09-04T01:44:44.137567Z", + "shell.execute_reply": "2026-09-04T01:44:44.137191Z" } }, "outputs": [ @@ -336,12 +337,12 @@ "text": [ "token_type: DPoP | expires_in: 3600 s\n", "{\n", - " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-3eda01\",\n", + " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-e54982\",\n", " \"trust_level\": \"first_party\",\n", - " \"mission_id\": \"bb887ec2-a37b-4fa3-8c16-2a1f06a1a320\",\n", - " \"jti\": \"bb887ec2-a37b-4fa3-8c16-2a1f06a1a320\"\n", + " \"mission_id\": \"2199e5ed-da96-4b07-a27a-9b124cbfa220\",\n", + " \"jti\": \"2199e5ed-da96-4b07-a27a-9b124cbfa220\"\n", "}\n", - "cnf: {'jkt': 'ysc8iRpwfyZEeIqh4sLz3q-Dw8DXmeIsZ1xf4ypq6Fs'}\n" + "cnf: {'jkt': 'RvPlc3-tDIahQ10Jk8rQt-G1NU6XkVXD7b8j4_Z_D8Q'}\n" ] } ], @@ -361,7 +362,7 @@ }, { "cell_type": "markdown", - "id": "d3ac0c74", + "id": "85a9a23a", "metadata": {}, "source": [ "A replayed proof must be rejected — the `jti` ledger is atomic (ODIS's replay concern\n", @@ -371,13 +372,13 @@ { "cell_type": "code", "execution_count": 7, - "id": "053a159d", + "id": "bf17a225", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:22:39.460403Z", - "iopub.status.busy": "2026-09-03T23:22:39.460318Z", - "iopub.status.idle": "2026-09-03T23:22:39.465625Z", - "shell.execute_reply": "2026-09-03T23:22:39.465170Z" + "iopub.execute_input": "2026-09-04T01:44:44.138723Z", + "iopub.status.busy": "2026-09-04T01:44:44.138645Z", + "iopub.status.idle": "2026-09-04T01:44:44.143704Z", + "shell.execute_reply": "2026-09-04T01:44:44.143313Z" } }, "outputs": [ @@ -402,7 +403,7 @@ }, { "cell_type": "markdown", - "id": "8250a675", + "id": "8e0e0613", "metadata": {}, "source": [ "## 6 · Delegation Record — Layer 2 · The Bridge (ODIS §6.3, Pillar 4, L2-01/05/06)\n", @@ -419,13 +420,13 @@ { "cell_type": "code", "execution_count": 8, - "id": "dadaa445", + "id": "d585fde0", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:22:39.466694Z", - "iopub.status.busy": "2026-09-03T23:22:39.466607Z", - "iopub.status.idle": "2026-09-03T23:22:39.485613Z", - "shell.execute_reply": "2026-09-03T23:22:39.485206Z" + "iopub.execute_input": "2026-09-04T01:44:44.144908Z", + "iopub.status.busy": "2026-09-04T01:44:44.144823Z", + "iopub.status.idle": "2026-09-04T01:44:44.164297Z", + "shell.execute_reply": "2026-09-04T01:44:44.163871Z" } }, "outputs": [ @@ -434,29 +435,29 @@ "output_type": "stream", "text": [ "{\n", - " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/researcher-3eda01\",\n", + " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/researcher-e54982\",\n", " \"act\": {\n", - " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-3eda01\"\n", + " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-e54982\"\n", " },\n", " \"delegation_depth\": 1,\n", - " \"mission_id\": \"bb887ec2-a37b-4fa3-8c16-2a1f06a1a320\",\n", - " \"jti\": \"5274f642-58d8-4517-8235-1590e08ec032\"\n", + " \"mission_id\": \"2199e5ed-da96-4b07-a27a-9b124cbfa220\",\n", + " \"jti\": \"faa546a8-8ee0-4a77-a9ed-7ad801748d49\"\n", "}\n" ] } ], "source": [ "sub_key, sub_pem, _ = gen_key()\n", - "r = api(\"POST\", \"/api/v1/agents/register\", json={\n", + "r = api(\"POST\", \"/agents/register\", json={\n", " \"name\": \"Researcher\", \"external_id\": f\"researcher-{run}\", \"identity_type\": \"agent\",\n", " \"sub_type\": \"tool_agent\", \"allowed_scopes\": [\"data:read\"],\n", " \"created_by\": \"demo-admin@example.com\", \"public_key_pem\": sub_pem,\n", " \"credential_policy_id\": policy[\"id\"],\n", "})\n", "researcher = r.json()[\"identity\"]\n", - "att2 = api(\"POST\", \"/api/v1/attestation/submit\", json={\"identity_id\": researcher[\"id\"], \"level\": \"hardware\",\n", + "att2 = api(\"POST\", \"/attestation/submit\", json={\"identity_id\": researcher[\"id\"], \"level\": \"hardware\",\n", " \"proof_type\": \"image_hash\", \"proof_value\": \"sha256:cafef00d\"}).json()\n", - "api(\"POST\", \"/api/v1/attestation/verify\", json={\"attestation_id\": att2[\"id\"]})\n", + "api(\"POST\", \"/attestation/verify\", json={\"attestation_id\": att2[\"id\"]})\n", "\n", "actor_token = jwt.encode({\"iss\": researcher[\"wimse_uri\"], \"sub\": researcher[\"wimse_uri\"], \"aud\": BASE,\n", " \"iat\": int(time.time()), \"exp\": int(time.time()) + 300}, sub_key, algorithm=\"ES256\")\n", @@ -469,7 +470,7 @@ }, { "cell_type": "markdown", - "id": "7a8b9649", + "id": "7e036ab2", "metadata": {}, "source": [ "Attenuation is monotonic and fails closed: the researcher's ceiling is `data:read`,\n", @@ -481,13 +482,13 @@ { "cell_type": "code", "execution_count": 9, - "id": "cd333e1e", + "id": "3dcfc549", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:22:39.486662Z", - "iopub.status.busy": "2026-09-03T23:22:39.486586Z", - "iopub.status.idle": "2026-09-03T23:22:39.491738Z", - "shell.execute_reply": "2026-09-03T23:22:39.491273Z" + "iopub.execute_input": "2026-09-04T01:44:44.165338Z", + "iopub.status.busy": "2026-09-04T01:44:44.165261Z", + "iopub.status.idle": "2026-09-04T01:44:44.170246Z", + "shell.execute_reply": "2026-09-04T01:44:44.169822Z" } }, "outputs": [ @@ -513,7 +514,7 @@ }, { "cell_type": "markdown", - "id": "9bd291ba", + "id": "f8a1a30f", "metadata": {}, "source": [ "## 7 · Bounded asynchronous human authorization — Layer 2 · The Bridge (ODIS-L2-02, Pillar 1)\n", @@ -536,13 +537,13 @@ { "cell_type": "code", "execution_count": 10, - "id": "c92794d7", + "id": "305b0030", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:22:39.492762Z", - "iopub.status.busy": "2026-09-03T23:22:39.492690Z", - "iopub.status.idle": "2026-09-03T23:22:39.805876Z", - "shell.execute_reply": "2026-09-03T23:22:39.805369Z" + "iopub.execute_input": "2026-09-04T01:44:44.171262Z", + "iopub.status.busy": "2026-09-04T01:44:44.171187Z", + "iopub.status.idle": "2026-09-04T01:44:44.487575Z", + "shell.execute_reply": "2026-09-04T01:44:44.486715Z" } }, "outputs": [ @@ -552,7 +553,7 @@ "text": [ "bc-authorize: 200\n", "{\n", - " \"auth_req_id\": \"Tt0ijnQck9IF4IdeXEhNlZK9Cy7zRp-avS3RWVmfKVU\",\n", + " \"auth_req_id\": \"dUSvAUpFS9bZOBaUKdH4dgjua9TgW90mBfNSs-Nty8k\",\n", " \"expires_in\": 300,\n", " \"interval\": 5\n", "}\n" @@ -572,11 +573,11 @@ } ], "source": [ - "alice_agent = api(\"POST\", \"/api/v1/identities\", json={\n", + "alice_agent = api(\"POST\", \"/identities\", json={\n", " \"external_id\": f\"alice-proxy-{run}\", \"owner_user_id\": \"alice@example.com\",\n", " \"identity_type\": \"agent\", \"sub_type\": \"human_proxy\",\n", " \"allowed_scopes\": [\"data:read\"], \"trust_level\": \"first_party\"}).json()\n", - "cl = api(\"POST\", \"/api/v1/oauth/clients\", json={\n", + "cl = api(\"POST\", \"/oauth/clients\", json={\n", " \"client_id\": f\"alice-proxy-{run}\", \"name\": \"Alice's proxy agent\", \"confidential\": True,\n", " \"identity_id\": alice_agent[\"id\"], \"grant_types\": [\"urn:openid:params:grant-type:ciba\"],\n", " \"scopes\": [\"data:read\"]}).json()\n", @@ -593,7 +594,7 @@ " \"client_id\": cl[\"client\"][\"client_id\"], \"client_secret\": cl[\"client_secret\"]})\n", "print(\"poll before approval:\", poll().json()[\"error\"])\n", "\n", - "api(\"POST\", f\"/api/v1/oauth2/bc-authorize/{auth_req['auth_req_id']}/approve\",\n", + "api(\"POST\", f\"/oauth2/bc-authorize/{auth_req['auth_req_id']}/approve\",\n", " json={\"subject_id\": \"user-alice-001\", \"subject_email\": \"alice@example.com\", \"subject_name\": \"Alice\"})\n", "ciba_token = poll().json()\n", "cc2 = claims_of(ciba_token[\"access_token\"])\n", @@ -603,7 +604,7 @@ }, { "cell_type": "markdown", - "id": "25b601cf", + "id": "04b6379a", "metadata": {}, "source": [ "## 8 · Confirmed compromise → cascade revocation — ODIS-L1-12, L3-04 / L3-05\n", @@ -617,13 +618,13 @@ { "cell_type": "code", "execution_count": 11, - "id": "21073278", + "id": "e005e54e", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:22:39.807263Z", - "iopub.status.busy": "2026-09-03T23:22:39.807146Z", - "iopub.status.idle": "2026-09-03T23:22:39.819883Z", - "shell.execute_reply": "2026-09-03T23:22:39.819408Z" + "iopub.execute_input": "2026-09-04T01:44:44.489937Z", + "iopub.status.busy": "2026-09-04T01:44:44.489740Z", + "iopub.status.idle": "2026-09-04T01:44:44.502401Z", + "shell.execute_reply": "2026-09-04T01:44:44.501797Z" } }, "outputs": [ @@ -631,13 +632,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "delegated token after cascade: {'active': False} (10 ms signal→dead)\n" + "delegated token after cascade: {'active': False} (9 ms signal→dead)\n" ] } ], "source": [ "t0 = time.time()\n", - "api(\"POST\", \"/api/v1/signals/ingest\", json={\n", + "api(\"POST\", \"/signals/ingest\", json={\n", " \"identity_id\": orch_id, \"signal_type\": \"anomalous_behavior\", \"severity\": \"critical\",\n", " \"source\": \"notebook-demo\", \"payload\": {\"reason\": \"prompt injection detected\"}})\n", "child_state = requests.post(BASE + \"/oauth2/token/introspect\", json={\"token\": delegated}).json()\n", @@ -647,7 +648,7 @@ }, { "cell_type": "markdown", - "id": "672316f2", + "id": "5357845e", "metadata": {}, "source": [ "## 9 · Audit lineage survives the kill — ODIS-CC-01 / CC-02\n", @@ -660,13 +661,13 @@ { "cell_type": "code", "execution_count": 12, - "id": "6caed8c2", + "id": "a8cb1217", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:22:39.821175Z", - "iopub.status.busy": "2026-09-03T23:22:39.821092Z", - "iopub.status.idle": "2026-09-03T23:22:39.827624Z", - "shell.execute_reply": "2026-09-03T23:22:39.827222Z" + "iopub.execute_input": "2026-09-04T01:44:44.503774Z", + "iopub.status.busy": "2026-09-04T01:44:44.503608Z", + "iopub.status.idle": "2026-09-04T01:44:44.510696Z", + "shell.execute_reply": "2026-09-04T01:44:44.510047Z" } }, "outputs": [ @@ -674,7 +675,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "chain: orch-3eda01 → researcher-3eda01\n", + "chain: orch-e54982 → researcher-e54982\n", "[\n", " {\n", " \"grant_type\": \"api_key\",\n", @@ -689,7 +690,7 @@ " ],\n", " \"attenuated\": [],\n", " \"is_revoked\": true,\n", - " \"revoke_reason\": \"auto-revoked by CAE signal fbb10756-71b2-44e7-a4f3-cfd2d4cf51e8 (severity: critical)\"\n", + " \"revoke_reason\": \"auto-revoked by CAE signal 5029913d-fd6b-46fa-aa48-57f4ee7c6393 (severity: critical)\"\n", " },\n", " {\n", " \"grant_type\": \"token_exchange\",\n", @@ -705,14 +706,14 @@ " \"data:write\"\n", " ],\n", " \"is_revoked\": true,\n", - " \"revoke_reason\": \"auto-revoked by CAE signal fbb10756-71b2-44e7-a4f3-cfd2d4cf51e8 (severity: critical)\"\n", + " \"revoke_reason\": \"auto-revoked by CAE signal 5029913d-fd6b-46fa-aa48-57f4ee7c6393 (severity: critical)\"\n", " }\n", "]\n" ] } ], "source": [ - "g = api(\"GET\", f\"/api/v1/delegations/by-jti/{dc['jti']}\").json()\n", + "g = api(\"GET\", f\"/delegations/by-jti/{dc['jti']}\").json()\n", "print(\"chain:\", \" → \".join(n[\"wimse_uri\"].split(\"/\")[-1] for n in g[\"nodes\"]))\n", "show([{k: e[k] for k in (\"grant_type\", \"delegation_depth\", \"scopes_in\", \"scopes_out\",\n", " \"attenuated\", \"is_revoked\", \"revoke_reason\")} for e in g[\"edges\"]])" @@ -720,7 +721,7 @@ }, { "cell_type": "markdown", - "id": "8d875a62", + "id": "1378c136", "metadata": {}, "source": [ "## 10 · The sponsor leaves — offboarding kill switch (ODIS-L1-06 / L1-10 / L3-05)\n", @@ -741,13 +742,13 @@ { "cell_type": "code", "execution_count": 13, - "id": "056aa5e9", + "id": "64334023", "metadata": { "execution": { - "iopub.execute_input": "2026-09-03T23:22:39.828904Z", - "iopub.status.busy": "2026-09-03T23:22:39.828826Z", - "iopub.status.idle": "2026-09-03T23:22:39.843953Z", - "shell.execute_reply": "2026-09-03T23:22:39.843475Z" + "iopub.execute_input": "2026-09-04T01:44:44.511983Z", + "iopub.status.busy": "2026-09-04T01:44:44.511882Z", + "iopub.status.idle": "2026-09-04T01:44:44.528914Z", + "shell.execute_reply": "2026-09-04T01:44:44.528351Z" } }, "outputs": [ @@ -756,7 +757,7 @@ "output_type": "stream", "text": [ "{\n", - " \"$schema\": \"http://localhost:8899/schemas/OffboardByOwnerOutputBody.json\",\n", + " \"$schema\": \"http://localhost:8899/OffboardByOwnerOutputBody.json\",\n", " \"identities_deactivated\": 1,\n", " \"credentials_revoked\": 0\n", "}\n", @@ -772,20 +773,20 @@ } ], "source": [ - "r = api(\"POST\", \"/api/v1/identities/offboard-by-owner\", json={\"owner_user_id\": \"alice@example.com\"})\n", + "r = api(\"POST\", \"/identities/offboard-by-owner\", json={\"owner_user_id\": \"alice@example.com\"})\n", "show(r.json())\n", "dead = requests.post(BASE + \"/oauth2/token/introspect\", json={\"token\": ciba_token[\"access_token\"]}).json()\n", "print(\"alice's CIBA-approved token:\", dead)\n", "assert dead == {\"active\": False}\n", "\n", - "g = api(\"GET\", f\"/api/v1/delegations/by-jti/{cc2['jti']}\").json()\n", + "g = api(\"GET\", f\"/delegations/by-jti/{cc2['jti']}\").json()\n", "show([{k: e[k] for k in (\"grant_type\", \"is_revoked\", \"revoke_reason\")} for e in g[\"edges\"]])\n", "assert all(e[\"is_revoked\"] for e in g[\"edges\"])" ] }, { "cell_type": "markdown", - "id": "8486c422", + "id": "966c79af", "metadata": {}, "source": [ "## What you just saw, in ODIS terms\n", From 1a500ee3a80cff823d6b32beaa56995153d8973a Mon Sep 17 00:00:00 2001 From: Sharath Rajasekar Date: Fri, 4 Sep 2026 10:08:40 -0700 Subject: [PATCH 08/14] devops: harden the notebook credential lint against silent no-op git ls-files supplies the target list (a moved notebook dir can't turn into an unmatched shell glob), an empty list is an error, and grep exit codes other than 0/1 fail the step instead of reading as clean. Co-Authored-By: Claude Fable 5 --- .github/workflows/pr-check.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index b0c782e5..7da5c7b0 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -266,10 +266,20 @@ jobs: # of printing decoded claims / selected fields only. - name: Lint committed notebook outputs for credential material shell: bash + # git ls-files (not a shell glob) so a moved/renamed notebook dir can + # never silently disarm the lint: grep exit 2 (error) fails the step + # instead of reading as "no match", and an empty file list is an error. run: | - if grep -RInE 'eyJ[A-Za-z0-9_-]{20,}|zid_sk_[A-Za-z0-9]|BEGIN[A-Z ]*PRIVATE KEY' \ - examples/*.ipynb examples/odis/*.ipynb; then + files=$(git ls-files 'examples/**/*.ipynb' 'examples/*.ipynb') + if [ -z "$files" ]; then + echo "::error::no committed notebooks found — lint target list is broken"; exit 1 + fi + status=0 + grep -InE 'eyJ[A-Za-z0-9_-]{20,}|zid_sk_[A-Za-z0-9]|BEGIN[A-Z ]*PRIVATE KEY' $files || status=$? + if [ "$status" -eq 0 ]; then echo "::error::credential-looking material found in committed notebook(s)"; exit 1 + elif [ "$status" -ne 1 ]; then + echo "::error::credential lint failed to run (grep exit $status)"; exit "$status" fi - name: Execute the quickstart + ODIS notebooks against the live server From 52b6231c8f39248793782b6d16aadc7d0e74c161 Mon Sep 17 00:00:00 2001 From: Sharath Rajasekar Date: Fri, 4 Sep 2026 11:00:06 -0700 Subject: [PATCH 09/14] docs: make the ODIS attenuation demo policy-driven and fix statement evidence pointers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review follow-ups on the ODIS material: - The raw walkthrough's 'smuggled data:write refused' cell was refused by the deprecated identity.allowed_scopes dual-read fallback, not by the attached CredentialPolicy (whose ceiling permitted it) — and token exchange never refuses an out-of-ceiling request outright anyway: it narrows to the intersection and refuses only when nothing survives. The researcher now carries its own policy (allowed_scopes [data:read]) as the ceiling, the demo shows narrowing (data:read data:write → data:read, asserted) and empty-intersection refusal (invalid_scope, asserted), and the prose in both notebooks and the statement's L2-06 row describe exactly that. No demo step depends on the deprecated identity field any more. - SDK walkthrough: researcher registered via agents.register with a credential_policies.create ceiling (not identities.create + deprecated allowed_scopes); orchestrator registered in one call. - CIBA cell reads the real 'scopes' claim (was 'scope', silently dropped by the defensive filter); committed output now shows the granted scope. - Statement: phantom evidence pointers fixed — revoke_credentials_by_owner (not *_cascade), (*OAuthService).refreshToken (not refreshTokenGrant); L1-05 SDK pointer qualified as highflame-sdk (separate repo); CC-07 root-package test given its own go test invocation. - Lint: zid_(sk|rt)_[A-Za-z0-9_-]{20,} — catches base64url keys starting with - or _ and refresh tokens. - Top-level README links the ODIS statement and notebooks (intro + Standards); examples/odis README points at migration 044 for the stale default-policy CIBA grant instead of prescribing volume destruction. Both notebooks re-executed against a fresh compose build; outputs real, credential lint clean. Co-Authored-By: Claude Fable 5 --- .github/workflows/pr-check.yml | 2 +- README.md | 4 + docs/odis/role-capability-statement.md | 14 +- examples/odis/README.md | 9 +- examples/odis/odis-walkthrough-sdk.ipynb | 142 ++++++------- examples/odis/odis-walkthrough.ipynb | 252 +++++++++++++---------- 6 files changed, 227 insertions(+), 196 deletions(-) diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 7da5c7b0..72f58e37 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -275,7 +275,7 @@ jobs: echo "::error::no committed notebooks found — lint target list is broken"; exit 1 fi status=0 - grep -InE 'eyJ[A-Za-z0-9_-]{20,}|zid_sk_[A-Za-z0-9]|BEGIN[A-Z ]*PRIVATE KEY' $files || status=$? + grep -InE 'eyJ[A-Za-z0-9_-]{20,}|zid_(sk|rt)_[A-Za-z0-9_-]{20,}|BEGIN[A-Z ]*PRIVATE KEY' $files || status=$? if [ "$status" -eq 0 ]; then echo "::error::credential-looking material found in committed notebook(s)"; exit 1 elif [ "$status" -ne 1 ]; then diff --git a/README.md b/README.md index ab8688f7..1538a90c 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,8 @@ Prefer a runnable walkthrough after installing the SDK? Open the [ZeroID Quickst Want a LangChain-specific intro? Open the [Scope-Aware Tools notebook](examples/langchain/scope_aware_tools.ipynb) to see the same agent gain or lose tool access purely by changing its ZeroID token. +Mapping ZeroID to the [ODIS draft](https://github.com/cosai-oasis/ws4-odis/blob/main/RFCs/ODIS.md) (CoSAI/OASIS WS4)? The [role-capability statement](docs/odis/role-capability-statement.md) grades every Layer 1–2 requirement against code and tests, and the [ODIS walkthrough notebooks](examples/odis/) run the same story — Agent Registration Record → Agent Runtime Credential → Delegation Record → cascade revocation — against a local instance with real outputs. + **Run ZeroID locally** (Docker — 30 seconds): ```bash @@ -955,6 +957,8 @@ References: [OpenID Agentic AI](https://openid.net/wp-content/uploads/2025/10/Id The extensions ZeroID layers on these baseline specs — the additional JWT claims, the CIBA `group_hint` parameter, the DPoP `bh` claim, RFC 8693 attenuation/depth semantics, the WIMSE URI scheme, reserved-claims gating, CAE signal types, and the non-standard endpoints — are specified normatively in [`docs/spec/zeroid-oauth-extensions.md`](docs/spec/zeroid-oauth-extensions.md). +How the whole stack maps onto the CoSAI/OASIS **ODIS** draft (Layers 1–2, native mode), including the requirements ZeroID does *not* meet, is in [`docs/odis/role-capability-statement.md`](docs/odis/role-capability-statement.md), with executable companions in [`examples/odis/`](examples/odis/). + | Standard | RFC / Spec | Used For | |----------|-----------|----------| | OAuth 2.1 | RFC 6749 + BCP | Foundational auth framework | diff --git a/docs/odis/role-capability-statement.md b/docs/odis/role-capability-statement.md index 9e7dd423..ef0a7197 100644 --- a/docs/odis/role-capability-statement.md +++ b/docs/odis/role-capability-statement.md @@ -125,8 +125,8 @@ style REV fill:#f0f0f0,color:#333,stroke:#999 | **ODIS-L1-02** Software Attestation | **Gap** | `domain/attestation.go` declares `image_hash`; only `internal/attestation/stub.go` `DevStubVerifier` backs it | No digest/signature/provenance verification ships in the core (no sigstore/in-toto/SLSA integration). The deliberate extension point exists: the `internal/attestation` `Verifier` registry dispatches by proof type, and `image_hash` is reserved for exactly this — a deployer or distribution binds a real provenance verifier there without core changes. | | **ODIS-L1-03** Runtime/Workload Attestation | **Meets** | `internal/attestation/oidc.go` (generic `OIDCVerifier`: JWKS discovery, issuer allow-list, audience, `required_claims`, SSRF-guarded transport); per-tenant `AttestationPolicy` (`internal/attestation/policy.go`); fail-closed: `TestAttestationFailsClosedWithNoPolicy`, `TestAttestationOIDCVerifierRejectsUntrustedIssuer`, `TestAttestationDoubleVerifyIsRejected` | One generic OIDC verifier; GitHub Actions, GCP Workload Identity Federation, and Kubernetes projected SA tokens are worked *configurations* (`docs/attestation.md`), not per-provider code. | | **ODIS-L1-04** Hardware Attestation | **Gap** | `tpm` proof type exists; stub-only | Same status and same extension point as L1-02: bind a TPM/TEE verifier into the registry. | -| **ODIS-L1-05** Credential Lifecycle | **Meets** | `domain/credential_policy.go` (`MaxTTLSeconds`, default 3600), enforcement `internal/service/credential_policy.go` `EnforcePolicy`; child exp clamped to parent (`TestTokenExchangeChildClampedToParentExpiry`); refresh rotation with reuse-detection family revocation (`tests/integration/refresh_token_race_test.go`) | Lifetimes are configurable, finite, policy-bounded. Automatic rotation before expiry is enforced by the SDK component of the declared target: the token manager refreshes ahead of expiry via a built-in buffer (`highflame/zeroid/_token_manager.py`, `_TOKEN_REFRESH_BUFFER`), with the server re-gating identity state and current policy on every rotation. | -| **ODIS-L1-06** Provisioning & De-provisioning | **Meets** | Lifecycle states `discovered→pending→active→suspended→deactivated/expired` (`domain/identity.go` `CanTransitionTo`); adopt/dismiss (`internal/handler/identity.go`); offboard-by-owner cascade (`internal/service/identity.go` `OffboardOwner`, DB `revoke_credentials_by_owner_cascade`); `tests/integration/discovery_lifecycle_test.go` | Satisfied via the "equivalent lifecycle-management mechanisms" clause — SCIM protocol endpoints themselves are not implemented. | +| **ODIS-L1-05** Credential Lifecycle | **Meets** | `domain/credential_policy.go` (`MaxTTLSeconds`, default 3600), enforcement `internal/service/credential_policy.go` `EnforcePolicy`; child exp clamped to parent (`TestTokenExchangeChildClampedToParentExpiry`); refresh rotation with reuse-detection family revocation (`tests/integration/refresh_token_race_test.go`) | Lifetimes are configurable, finite, policy-bounded. Automatic rotation before expiry is enforced by the SDK component of the declared target: the token manager refreshes ahead of expiry via a built-in buffer (highflame-sdk 0.3.23 (separate repo): `highflame/zeroid/_token_manager.py`, `_TOKEN_REFRESH_BUFFER`), with the server re-gating identity state and current policy on every rotation. | +| **ODIS-L1-06** Provisioning & De-provisioning | **Meets** | Lifecycle states `discovered→pending→active→suspended→deactivated/expired` (`domain/identity.go` `CanTransitionTo`); adopt/dismiss (`internal/handler/identity.go`); offboard-by-owner cascade (`internal/service/identity.go` `OffboardOwner`, DB `revoke_credentials_by_owner`); `tests/integration/discovery_lifecycle_test.go` | Satisfied via the "equivalent lifecycle-management mechanisms" clause — SCIM protocol endpoints themselves are not implemented. | | **ODIS-L1-07** Federated Trust *(Extended)* | **Meets** | Direct external-issuer OIDC federation: `external_issuers` config (`domain/external_issuer.go`), verification `internal/service/oauth_external_idp.go` (`subject_token_type=…:id_token`), upstream `iss` propagated as `user_id_iss`; `TestExternalIDTokenFederation_EndToEnd`, `_KeyRotation`, `_CrossTenantRejected` | Deployer-configured trust anchors; no automatic federation discovery. A trusted-service broker path also exists (`ExternalPrincipalExchange`) and is documented as the lossier fallback. | | **ODIS-L1-08** Trusted Distribution | **Gap** | — | No supply-chain verification before identity issuance. Pairs with L1-02. | | **ODIS-L1-09** Holder-of-Key Authentication | **Meets (via configuration)** | DPoP with atomic jti replay ledger (`pkg/dpop/verifier.go`, `TestDPoPReplayRejected`); refresh tokens key-bound (`TestDPoPRefreshBoundWithDifferentKeyRejected`); binding propagates through delegation (`TestDPoPTokenExchange_PropagatesBindingToSubAgent`); WIMSE single-use nonce-bound proof tokens (`internal/service/proof.go`); **enforcement switch**: `token.require_dpop` refuses proof-less issuance with `invalid_dpop_proof` on every grant and advertises `dpop_bound_access_tokens_required` per RFC 9449 §5.1 (`TestRequireDPoPRefusesProoflessIssuance`, `TestRequireDPoPAdvertisedInASMetadata`) | The conformant posture is `token.require_dpop: true` (default off preserves Bearer opt-in): every issued credential is then proof-of-possession-bound. Client-side note: the Python SDK (0.3.23) does not yet construct DPoP proofs (tracked upstream, sdk#105), so a require_dpop deployment currently serves raw-HTTP/self-signing clients — the notebook demonstrates proof construction in a few lines of PyJWT. Consequently the SDK companion notebook runs under the default `require_dpop: false`; each notebook's required configuration is stated in `examples/odis/README.md`. | @@ -140,11 +140,11 @@ style REV fill:#f0f0f0,color:#333,stroke:#999 |---|---|---|---| | **ODIS-L2-01** Delegated Authorization | **Partial** | Three-way scope intersection at exchange (`internal/service/oauth.go` `tokenExchange`: requested ∩ subject-granted ∩ actor-policy); empty intersection fails closed (`invalid_scope`) | Partial under a strict reading: the MUST enumerates the intersection's inputs as principal ∩ registration ∩ parent ∩ **task ∩ resource ∩ environmental constraints** ∩ adapter mapping. ZeroID intersects scopes/depth/TTL, resolves the registration, clamps to the parent, and re-verifies the principal on refresh — but models no task/resource/constraint dimensions. Fail-closed behavior itself is complete. | | **ODIS-L2-02** Bounded Authorization | **Meets** | CIBA poll/ping/push (`internal/service/backchannel.go`, `TestCIBACore1_0_S11_AuthorizationPendingWhilePending`); RAR typed `authorization_details` bound into the approval and the issued JWT (`TestRFC9396_S6_1_AccessTokenJWTEmbedsAuthorizationDetails`, `ciba_rar_test.go`) | Async human approval bound to the request's declared authority — ODIS's headless-agent clause implemented with standard protocols. | -| **ODIS-L2-03** Session Continuity | **Meets** | Refresh re-gates identity usability before *and* after rotation (`internal/service/oauth.go` `refreshTokenGrant`); DPoP binding re-checked; reuse detection revokes the family (`refresh_token_race_test.go`) | Fail-closed on revocation, deactivation, expiry, or binding mismatch. | +| **ODIS-L2-03** Session Continuity | **Meets** | Refresh re-gates identity usability before *and* after rotation (`internal/service/oauth.go` `(*OAuthService).refreshToken`); DPoP binding re-checked; reuse detection revokes the family (`refresh_token_race_test.go`) | Fail-closed on revocation, deactivation, expiry, or binding mismatch. | | **ODIS-L2-04** Durable Delegation *(Extended)* | **Meets** | Refresh tokens with rotation + reuse-detection (`refresh_token_race_test.go`); every refresh re-gates identity state and re-runs current policy | This is the SHOULD's substance: a pre-authorized window in which authority auto-renews without new human interaction, bounded by lifecycle state, policy, and revocation — the window construct is the refresh-token family. | | **ODIS-L2-05** Delegation Record | **Partial** | `act` chain (`domain/token.go` `ActorClaims`, `TestRFC8693_S4_2_ActClaimChainsDelegation`), `parent_jti`, `mission_id` lineage (`TestMissionID_ChainPropagation`), `delegation_depth`, child expiry clamped to parent | Carried as JWT claims (§6.3 permits any integrity-protected carrier; integrity = the AS signature), but several §6.3 MUST fields have no equivalent — see the field mapping below the table. | -| **ODIS-L2-06** Authorization Attenuation | **Partial** | Monotonic narrowing via set intersection over a controlled scope vocabulary; depth cap; `TestRFC8693_*` + `subagent_delegation_test.go` | This is lexical-with-controlled-vocabulary, not the semantic `attenuation_profile_ref` mechanism ODIS specifies. Honest reading: sufficient when the issuer owns the vocabulary, insufficient for cross-vendor scope semantics. | -| **ODIS-L2-07** Contextual Re-verification | **Meets** | Pre-rotation identity gate + full `EnforcePolicy` re-run against *current* policy on every refresh (`refreshTokenGrant`; `deactivation_test.go`) | | +| **ODIS-L2-06** Authorization Attenuation | **Partial** | Monotonic narrowing via set intersection over a controlled scope vocabulary (an out-of-ceiling request is narrowed to the intersection, and refused only when nothing survives — `internal/service/oauth.go` token-exchange Step 4); depth cap; `TestRFC8693_*` + `subagent_delegation_test.go` | This is lexical-with-controlled-vocabulary, not the semantic `attenuation_profile_ref` mechanism ODIS specifies. Honest reading: sufficient when the issuer owns the vocabulary, insufficient for cross-vendor scope semantics. | +| **ODIS-L2-07** Contextual Re-verification | **Meets** | Pre-rotation identity gate + full `EnforcePolicy` re-run against *current* policy on every refresh (`(*OAuthService).refreshToken`; `deactivation_test.go`) | | | **ODIS-L2-08** Backward Compatibility (bridge) | **Gap** | — | ZeroID is native-mode only: downstream services validate ZeroID JWTs (`pkg/authjwt`). Target-native credential translation is a companion credential-broker component under development, out of this statement's scope. | | **ODIS-L2-09** Bridge Mapping | **Gap** | — | With L2-08. | | **ODIS-L2-10** Fail-Closed Attenuation (bridge) | **Gap** | — | With L2-08. | @@ -187,7 +187,7 @@ profile the spec could publish. | ODIS-L3-02 Governance Checkpoint | **Gap** — belongs to a gateway/router component, not the AS | | ODIS-L3-03 Velocity Limits | **Gap** — a dormant `rate_limit_rps` column exists and is read nowhere; CIBA `slow_down` is protocol pacing, not rate limiting | | ODIS-L3-04 Revocation Latency | **Partial** — the *mechanism* is synchronous cascade in-transaction + SSE push (`GET /signals/stream`) + `RevocationNotifier` hooks; a declared, measured maximum latency (what ODIS actually requires) is not published — see CC-03 | -| ODIS-L3-05 Kill Switch | **Meets** — identity deactivation and offboard-by-owner cascade-revoke all credentials in one operation (`revoke_credentials_by_owner_cascade`, `TestCAESignalRevokesAllActiveCredentials`) | +| ODIS-L3-05 Kill Switch | **Meets** — identity deactivation and offboard-by-owner cascade-revoke all credentials in one operation (`revoke_credentials_by_owner`, `TestCAESignalRevokesAllActiveCredentials`) | | ODIS-L3-06 Policy Engine Integration | **Gap** — no OPA/Cedar callout in ZeroID's request path; ID-JAG maps IdP claims into Cedar-shaped principal attributes for *downstream* engines (`oauth_id_jag.go`), which is claim shaping, not checkpoint integration. The §6.4 object itself is assemblable from existing APIs — see the inter-layer payload appendix | | ODIS-L3-07 Task-Bound Tokens *(Extended)* | **Partial** — `mission_id` (delegation-tree purpose anchor) and RAR `authorization_details` carry declared intent in the token; no checkpoint validates actions against it inside ZeroID | | ODIS-L3-08 Boundary Protection *(Extended)* | **Gap** — with L3-02 | @@ -202,7 +202,7 @@ profile the spec could publish. | **ODIS-CC-04** Availability | **Gap** | No published availability objective/measurement | With CC-03. | | **ODIS-CC-05** Governed Identity Creation | **Meets** | Registration via authenticated admin surface; discovered rows require human adopt before usability; agents cannot mint or expand their own registration (`AdoptIdentity`, transition matrix) | | | **ODIS-CC-06** Terminal Exchange Audit Anchor | **N/A** | ID-JAG redemptions record observed resources with single-use jti enforcement (`internal/store/postgres/id_jag_jti.go`) | Terminal exchange into non-ODIS systems is the broker component's obligation, not the AS's. | -| **ODIS-CC-07** Data Protection | **Meets** | Query strings stripped from access logs (`logSafePath`, `log_redaction_test.go`); OAuth error-value redaction on token/introspect/revoke/bc-authorize (`internal/handler/routes.go` `redactErrorValues`); raw connector/API secrets never persisted (hashed/ referenced); configurable audit retention with deletion sweeper | | +| **ODIS-CC-07** Data Protection | **Meets** | Query strings stripped from access logs (`logSafePath`, `log_redaction_test.go` (root package — `go test . -run TestLogSafePath`)); OAuth error-value redaction on token/introspect/revoke/bc-authorize (`internal/handler/routes.go` `redactErrorValues`); raw connector/API secrets never persisted (hashed/ referenced); configurable audit retention with deletion sweeper | | ## Appendix — the inter-layer payload contract (§6.1 / §6.2 / §6.4) diff --git a/examples/odis/README.md b/examples/odis/README.md index 0e571a63..e0dc0cc9 100644 --- a/examples/odis/README.md +++ b/examples/odis/README.md @@ -57,11 +57,10 @@ claims and selected fields, never raw tokens, API keys, or private keys. CI enforces this with a credential-material lint and re-executes both notebooks against a fresh server on every PR (`highflame-notebook-check`). -Upgrading an existing checkout? Tenant default credential policies are -created once and don't self-heal: a postgres volume from before the CIBA -grant joined the defaults will refuse §7's bound-client redemption -(`access_denied`). `docker compose down -v` for a fresh volume, or add the -CIBA grant to the stored default policy. +Upgrading a checkout whose postgres volume predates the CIBA grant joining +the default credential policy? Current zeroid heals stored default policies +on startup (migration `044_default_policy_add_ciba_grant`); if you are +running an older build, `docker compose down -v` for a fresh volume. The notebook is re-runnable: every run registers fresh identities under a random suffix. The committed outputs are from a real run against a local diff --git a/examples/odis/odis-walkthrough-sdk.ipynb b/examples/odis/odis-walkthrough-sdk.ipynb index c2ca7347..7b6257e4 100644 --- a/examples/odis/odis-walkthrough-sdk.ipynb +++ b/examples/odis/odis-walkthrough-sdk.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "749b8cd0", + "id": "62d21988", "metadata": {}, "source": [ "# ZeroID as ODIS Layers 1–2 — the SDK companion\n", @@ -38,13 +38,13 @@ { "cell_type": "code", "execution_count": 1, - "id": "a70bb62b", + "id": "9634ed4a", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T01:44:45.277918Z", - "iopub.status.busy": "2026-09-04T01:44:45.277673Z", - "iopub.status.idle": "2026-09-04T01:44:45.423450Z", - "shell.execute_reply": "2026-09-04T01:44:45.423045Z" + "iopub.execute_input": "2026-09-04T17:59:24.452141Z", + "iopub.status.busy": "2026-09-04T17:59:24.452035Z", + "iopub.status.idle": "2026-09-04T17:59:24.573608Z", + "shell.execute_reply": "2026-09-04T17:59:24.573249Z" } }, "outputs": [ @@ -52,7 +52,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "status='healthy' service='zeroid' timestamp=datetime.datetime(2026, 9, 4, 1, 44, 45, 413077, tzinfo=TzInfo(0)) uptime_ms=12792 | run id: 5a2d4a\n" + "status='healthy' service='zeroid' timestamp=datetime.datetime(2026, 9, 4, 17, 59, 24, 570241, tzinfo=TzInfo(0)) uptime_ms=94152 | run id: 088aef\n" ] } ], @@ -70,7 +70,7 @@ }, { "cell_type": "markdown", - "id": "2113649a", + "id": "8b9f2400", "metadata": {}, "source": [ "## 1 · Agent Registration Record — Layer 1 · The Passport (ODIS §6.1)\n", @@ -78,20 +78,21 @@ "`agents.register` creates the durable governance record and returns the bootstrap API key\n", "in one call. The agent is born `unverified` with a stable WIMSE URI (ODIS's `agent_id`).\n", "\n", - "> **SDK coverage note:** the policy-gated fail-closed issuance demo lives in the raw-HTTP\n", - "> companion; this notebook stays on the target-side view." + "> **SDK coverage note:** the attestation-gated, fail-closed issuance demo (a policy\n", + "> *refusing* to issue) lives in the raw-HTTP companion; this notebook stays on the\n", + "> target-side view and uses a policy only as the delegation ceiling in §4." ] }, { "cell_type": "code", "execution_count": 2, - "id": "34514dca", + "id": "bef5847b", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T01:44:45.424626Z", - "iopub.status.busy": "2026-09-04T01:44:45.424524Z", - "iopub.status.idle": "2026-09-04T01:44:45.437524Z", - "shell.execute_reply": "2026-09-04T01:44:45.437080Z" + "iopub.execute_input": "2026-09-04T17:59:24.575019Z", + "iopub.status.busy": "2026-09-04T17:59:24.574922Z", + "iopub.status.idle": "2026-09-04T17:59:24.586734Z", + "shell.execute_reply": "2026-09-04T17:59:24.586362Z" } }, "outputs": [ @@ -99,21 +100,21 @@ "name": "stdout", "output_type": "stream", "text": [ - "spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-5a2d4a | trust: unverified | status: active\n" + "spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-088aef | trust: unverified | status: active\n" ] } ], "source": [ "reg = client.agents.register(name=\"Orchestrator\", external_id=f\"orch-{run}\",\n", - " sub_type=\"orchestrator\", created_by=\"demo-admin@example.com\")\n", + " sub_type=\"orchestrator\", created_by=\"demo-admin@example.com\",\n", + " allowed_scopes=[\"data:read\", \"data:write\"])\n", "orch = reg.identity\n", - "client.identities.update(orch.id, allowed_scopes=[\"data:read\", \"data:write\"])\n", "print(orch.wimse_uri, \"| trust:\", orch.trust_level, \"| status:\", orch.status)" ] }, { "cell_type": "markdown", - "id": "b530138c", + "id": "9707cd70", "metadata": {}, "source": [ "## 2 · Agent Runtime Credential — the Passport's output (ODIS §6.2, L1-05)\n", @@ -125,13 +126,13 @@ { "cell_type": "code", "execution_count": 3, - "id": "36b989f9", + "id": "3bdd6999", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T01:44:45.438597Z", - "iopub.status.busy": "2026-09-04T01:44:45.438525Z", - "iopub.status.idle": "2026-09-04T01:44:45.447689Z", - "shell.execute_reply": "2026-09-04T01:44:45.447249Z" + "iopub.execute_input": "2026-09-04T17:59:24.587881Z", + "iopub.status.busy": "2026-09-04T17:59:24.587810Z", + "iopub.status.idle": "2026-09-04T17:59:24.595667Z", + "shell.execute_reply": "2026-09-04T17:59:24.595235Z" } }, "outputs": [ @@ -150,7 +151,7 @@ }, { "cell_type": "markdown", - "id": "5d8a4618", + "id": "5e0526ad", "metadata": {}, "source": [ "## 3 · The ODIS-aware target — the Bridge's native mode (ODIS §2.5 / L2-15)\n", @@ -175,13 +176,13 @@ { "cell_type": "code", "execution_count": 4, - "id": "a59bdc93", + "id": "ba9787c8", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T01:44:45.448867Z", - "iopub.status.busy": "2026-09-04T01:44:45.448777Z", - "iopub.status.idle": "2026-09-04T01:44:45.456363Z", - "shell.execute_reply": "2026-09-04T01:44:45.455915Z" + "iopub.execute_input": "2026-09-04T17:59:24.596869Z", + "iopub.status.busy": "2026-09-04T17:59:24.596788Z", + "iopub.status.idle": "2026-09-04T17:59:24.602785Z", + "shell.execute_reply": "2026-09-04T17:59:24.602391Z" } }, "outputs": [ @@ -189,7 +190,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "verified: spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-5a2d4a | delegated: False\n", + "verified: spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-088aef | delegated: False\n", "refused (ZeroIDError): Missing required scope: 'admin:all'\n", "refused (ZeroIDError): Insufficient trust level: required 'first_party', got 'unverified'\n" ] @@ -210,7 +211,7 @@ }, { "cell_type": "markdown", - "id": "57d8b38f", + "id": "e8de1b59", "metadata": {}, "source": [ "Audience is the first duty on ODIS §2.5's native-mode list, and it is one argument\n", @@ -222,13 +223,13 @@ { "cell_type": "code", "execution_count": 5, - "id": "d5029aea", + "id": "a3195e2d", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T01:44:45.457398Z", - "iopub.status.busy": "2026-09-04T01:44:45.457320Z", - "iopub.status.idle": "2026-09-04T01:44:45.459927Z", - "shell.execute_reply": "2026-09-04T01:44:45.459532Z" + "iopub.execute_input": "2026-09-04T17:59:24.603925Z", + "iopub.status.busy": "2026-09-04T17:59:24.603836Z", + "iopub.status.idle": "2026-09-04T17:59:24.606521Z", + "shell.execute_reply": "2026-09-04T17:59:24.606123Z" } }, "outputs": [ @@ -253,7 +254,7 @@ }, { "cell_type": "markdown", - "id": "76d6841e", + "id": "d71077de", "metadata": {}, "source": [ "Trust is **minted into the credential at issuance** — `trust_level` is a claim. So\n", @@ -269,13 +270,13 @@ { "cell_type": "code", "execution_count": 6, - "id": "77b16706", + "id": "034f887d", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T01:44:45.460965Z", - "iopub.status.busy": "2026-09-04T01:44:45.460869Z", - "iopub.status.idle": "2026-09-04T01:44:45.470154Z", - "shell.execute_reply": "2026-09-04T01:44:45.469735Z" + "iopub.execute_input": "2026-09-04T17:59:24.607659Z", + "iopub.status.busy": "2026-09-04T17:59:24.607581Z", + "iopub.status.idle": "2026-09-04T17:59:24.618392Z", + "shell.execute_reply": "2026-09-04T17:59:24.617915Z" } }, "outputs": [ @@ -297,31 +298,30 @@ }, { "cell_type": "markdown", - "id": "9d56aaeb", + "id": "14d1dadf", "metadata": {}, "source": [ "## 4 · Delegation Record — Layer 2 · The Bridge (ODIS §6.3, Pillar 4, L2-05/06)\n", "\n", - "The researcher registers with its own holder key, proves possession of it in the\n", - "`actor_token` (a self-signed ES256 assertion — the SDK doesn't mint these; five lines of\n", - "PyJWT do), and the orchestrator delegates via RFC 8693. Attenuation is **monotonic and\n", - "visible**: the researcher's ceiling is `data:read`, so requesting `data:read data:write`\n", - "yields a token silently narrowed to `data:read` — and a request where *nothing* survives\n", - "the intersection is refused outright. (With a credential policy attached, any\n", - "out-of-ceiling request errors instead of narrowing — the stricter mode is in the raw\n", - "companion.)" + "The researcher registers with its own holder key and a **credential policy** whose\n", + "`allowed_scopes` is just `data:read` — that policy is the delegation ceiling. It proves\n", + "possession of the key in the `actor_token` (a self-signed ES256 assertion — the SDK doesn't\n", + "mint these; five lines of PyJWT do), and the orchestrator delegates via RFC 8693.\n", + "Attenuation is **monotonic and visible**: requesting `data:read data:write` yields a token\n", + "narrowed to `data:read` — never more than the ceiling — and a request where *nothing*\n", + "survives the intersection is refused outright." ] }, { "cell_type": "code", "execution_count": 7, - "id": "a55093bf", + "id": "8294fa35", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T01:44:45.471144Z", - "iopub.status.busy": "2026-09-04T01:44:45.471072Z", - "iopub.status.idle": "2026-09-04T01:44:45.484638Z", - "shell.execute_reply": "2026-09-04T01:44:45.484205Z" + "iopub.execute_input": "2026-09-04T17:59:24.619443Z", + "iopub.status.busy": "2026-09-04T17:59:24.619361Z", + "iopub.status.idle": "2026-09-04T17:59:24.636609Z", + "shell.execute_reply": "2026-09-04T17:59:24.636074Z" } }, "outputs": [ @@ -330,19 +330,23 @@ "output_type": "stream", "text": [ "granted scope (requested read+write): 'data:read'\n", - "sub: researcher-5a2d4a | is_delegated: True | act: {'sub': 'spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-5a2d4a'} | depth: 1\n", + "sub: researcher-088aef | is_delegated: True | act: {'sub': 'spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-088aef'} | depth: 1\n", "empty intersection refused (APIError): [400] invalid_scope: requested scopes are not available for delegation\n" ] } ], "source": [ + "ceiling = client.credential_policies.create(\n", + " name=f\"researcher-ceiling-{run}\", allowed_scopes=[\"data:read\"],\n", + " allowed_grant_types=[\"api_key\", \"token_exchange\"], max_delegation_depth=2)\n", + "\n", "priv = ec.generate_private_key(ec.SECP256R1())\n", "pub_pem = priv.public_key().public_bytes(serialization.Encoding.PEM,\n", " serialization.PublicFormat.SubjectPublicKeyInfo).decode()\n", - "researcher = client.identities.create(\n", - " external_id=f\"researcher-{run}\", owner_user_id=\"demo-admin@example.com\",\n", - " name=\"Researcher\", identity_type=\"agent\", sub_type=\"tool_agent\",\n", - " trust_level=\"first_party\", allowed_scopes=[\"data:read\"], public_key_pem=pub_pem)\n", + "researcher = client.agents.register(\n", + " name=\"Researcher\", external_id=f\"researcher-{run}\", sub_type=\"tool_agent\",\n", + " created_by=\"demo-admin@example.com\", trust_level=\"first_party\",\n", + " public_key_pem=pub_pem, credential_policy_id=ceiling.id).identity\n", "\n", "now = int(time.time())\n", "actor_token = jwt.encode({\"iss\": researcher.wimse_uri, \"sub\": researcher.wimse_uri,\n", @@ -366,7 +370,7 @@ }, { "cell_type": "markdown", - "id": "f8becf10", + "id": "a8e646cf", "metadata": {}, "source": [ "## 5 · Compromise signal, cascade — and what native mode must add — L1-12, L3-04/05\n", @@ -383,13 +387,13 @@ { "cell_type": "code", "execution_count": 8, - "id": "7c5b1f82", + "id": "fa65e76c", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T01:44:45.485807Z", - "iopub.status.busy": "2026-09-04T01:44:45.485720Z", - "iopub.status.idle": "2026-09-04T01:44:45.492259Z", - "shell.execute_reply": "2026-09-04T01:44:45.491804Z" + "iopub.execute_input": "2026-09-04T17:59:24.637723Z", + "iopub.status.busy": "2026-09-04T17:59:24.637635Z", + "iopub.status.idle": "2026-09-04T17:59:24.644378Z", + "shell.execute_reply": "2026-09-04T17:59:24.643955Z" } }, "outputs": [ @@ -414,7 +418,7 @@ }, { "cell_type": "markdown", - "id": "aae7dab2", + "id": "8233ffe9", "metadata": {}, "source": [ "## What the SDK view added\n", diff --git a/examples/odis/odis-walkthrough.ipynb b/examples/odis/odis-walkthrough.ipynb index 9a84121c..f70a9e5c 100644 --- a/examples/odis/odis-walkthrough.ipynb +++ b/examples/odis/odis-walkthrough.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "a5dfad0e", + "id": "60ad7e36", "metadata": {}, "source": [ "# ZeroID as ODIS Layers 1–2 — an executable walkthrough\n", @@ -48,13 +48,13 @@ { "cell_type": "code", "execution_count": 1, - "id": "b8619930", + "id": "461baa10", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T01:44:43.938778Z", - "iopub.status.busy": "2026-09-04T01:44:43.938631Z", - "iopub.status.idle": "2026-09-04T01:44:44.045024Z", - "shell.execute_reply": "2026-09-04T01:44:44.044538Z" + "iopub.execute_input": "2026-09-04T17:59:06.503146Z", + "iopub.status.busy": "2026-09-04T17:59:06.502960Z", + "iopub.status.idle": "2026-09-04T17:59:07.553629Z", + "shell.execute_reply": "2026-09-04T17:59:07.552942Z" } }, "outputs": [ @@ -62,7 +62,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "zeroid: healthy | run id: e54982\n" + "zeroid: healthy | run id: 6a49f5\n" ] } ], @@ -100,7 +100,7 @@ }, { "cell_type": "markdown", - "id": "895cea1c", + "id": "8d0669ea", "metadata": {}, "source": [ "## 1 · Agent Registration Record — Layer 1 · The Passport (ODIS §6.1)\n", @@ -115,13 +115,13 @@ { "cell_type": "code", "execution_count": 2, - "id": "8a1ed311", + "id": "574ada48", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T01:44:44.046304Z", - "iopub.status.busy": "2026-09-04T01:44:44.046178Z", - "iopub.status.idle": "2026-09-04T01:44:44.079582Z", - "shell.execute_reply": "2026-09-04T01:44:44.079089Z" + "iopub.execute_input": "2026-09-04T17:59:07.555670Z", + "iopub.status.busy": "2026-09-04T17:59:07.555493Z", + "iopub.status.idle": "2026-09-04T17:59:07.580263Z", + "shell.execute_reply": "2026-09-04T17:59:07.579681Z" } }, "outputs": [ @@ -130,7 +130,7 @@ "output_type": "stream", "text": [ "{\n", - " \"wimse_uri\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-e54982\",\n", + " \"wimse_uri\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-6a49f5\",\n", " \"identity_type\": \"agent\",\n", " \"trust_level\": \"unverified\",\n", " \"status\": \"active\",\n", @@ -144,7 +144,6 @@ "r = api(\"POST\", \"/agents/register\", json={\n", " \"name\": \"Orchestrator\", \"external_id\": f\"orch-{run}\",\n", " \"identity_type\": \"agent\", \"sub_type\": \"orchestrator\",\n", - " \"allowed_scopes\": [\"data:read\", \"data:write\"],\n", " \"created_by\": \"demo-admin@example.com\", # ODIS L1-10: accountable sponsor\n", " \"public_key_pem\": orch_pem, # holder public key (L1-09)\n", "})\n", @@ -154,7 +153,7 @@ }, { "cell_type": "markdown", - "id": "9d85eaed", + "id": "1111f5c6", "metadata": {}, "source": [ "## 2 · Registration-record governance fields — Layer 1 · The Passport (ODIS §6.1)\n", @@ -170,13 +169,13 @@ { "cell_type": "code", "execution_count": 3, - "id": "e30002b6", + "id": "68fbce6e", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T01:44:44.080661Z", - "iopub.status.busy": "2026-09-04T01:44:44.080584Z", - "iopub.status.idle": "2026-09-04T01:44:44.091892Z", - "shell.execute_reply": "2026-09-04T01:44:44.091419Z" + "iopub.execute_input": "2026-09-04T17:59:07.581532Z", + "iopub.status.busy": "2026-09-04T17:59:07.581430Z", + "iopub.status.idle": "2026-09-04T17:59:07.594817Z", + "shell.execute_reply": "2026-09-04T17:59:07.594245Z" } }, "outputs": [ @@ -185,7 +184,7 @@ "output_type": "stream", "text": [ "{\n", - " \"name\": \"odis-demo-e54982\",\n", + " \"name\": \"odis-demo-6a49f5\",\n", " \"max_ttl_seconds\": 3600,\n", " \"required_trust_level\": \"first_party\",\n", " \"max_delegation_depth\": 2\n", @@ -209,7 +208,7 @@ }, { "cell_type": "markdown", - "id": "4fe6c333", + "id": "36ed3c27", "metadata": {}, "source": [ "## 3 · No registration resolution, no authority — The Bridge refuses (ODIS-L2-14 + L1-11)\n", @@ -222,13 +221,13 @@ { "cell_type": "code", "execution_count": 4, - "id": "acbd64a2", + "id": "971b2ce6", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T01:44:44.092984Z", - "iopub.status.busy": "2026-09-04T01:44:44.092903Z", - "iopub.status.idle": "2026-09-04T01:44:44.101174Z", - "shell.execute_reply": "2026-09-04T01:44:44.100632Z" + "iopub.execute_input": "2026-09-04T17:59:07.596144Z", + "iopub.status.busy": "2026-09-04T17:59:07.596014Z", + "iopub.status.idle": "2026-09-04T17:59:07.604540Z", + "shell.execute_reply": "2026-09-04T17:59:07.603986Z" } }, "outputs": [ @@ -252,7 +251,7 @@ }, { "cell_type": "markdown", - "id": "ced783ad", + "id": "1fe42210", "metadata": {}, "source": [ "## 4 · Attestation raises trust — Layer 1 · The Passport (ODIS-L1-03 / L1-11)\n", @@ -269,13 +268,13 @@ { "cell_type": "code", "execution_count": 5, - "id": "5cc1557d", + "id": "e350aa0b", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T01:44:44.102394Z", - "iopub.status.busy": "2026-09-04T01:44:44.102316Z", - "iopub.status.idle": "2026-09-04T01:44:44.121071Z", - "shell.execute_reply": "2026-09-04T01:44:44.120581Z" + "iopub.execute_input": "2026-09-04T17:59:07.605714Z", + "iopub.status.busy": "2026-09-04T17:59:07.605629Z", + "iopub.status.idle": "2026-09-04T17:59:07.623717Z", + "shell.execute_reply": "2026-09-04T17:59:07.623252Z" } }, "outputs": [ @@ -300,7 +299,7 @@ }, { "cell_type": "markdown", - "id": "a7429310", + "id": "4d747fcd", "metadata": {}, "source": [ "## 5 · Agent Runtime Credential — the Passport's output (ODIS §6.2, L1-05 / L1-09)\n", @@ -321,13 +320,13 @@ { "cell_type": "code", "execution_count": 6, - "id": "ff51a6d5", + "id": "e138d67d", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T01:44:44.122211Z", - "iopub.status.busy": "2026-09-04T01:44:44.122126Z", - "iopub.status.idle": "2026-09-04T01:44:44.137567Z", - "shell.execute_reply": "2026-09-04T01:44:44.137191Z" + "iopub.execute_input": "2026-09-04T17:59:07.625240Z", + "iopub.status.busy": "2026-09-04T17:59:07.625141Z", + "iopub.status.idle": "2026-09-04T17:59:07.640374Z", + "shell.execute_reply": "2026-09-04T17:59:07.639903Z" } }, "outputs": [ @@ -337,12 +336,12 @@ "text": [ "token_type: DPoP | expires_in: 3600 s\n", "{\n", - " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-e54982\",\n", + " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-6a49f5\",\n", " \"trust_level\": \"first_party\",\n", - " \"mission_id\": \"2199e5ed-da96-4b07-a27a-9b124cbfa220\",\n", - " \"jti\": \"2199e5ed-da96-4b07-a27a-9b124cbfa220\"\n", + " \"mission_id\": \"a17223c6-885f-4258-ba21-3d579a78d348\",\n", + " \"jti\": \"a17223c6-885f-4258-ba21-3d579a78d348\"\n", "}\n", - "cnf: {'jkt': 'RvPlc3-tDIahQ10Jk8rQt-G1NU6XkVXD7b8j4_Z_D8Q'}\n" + "cnf: {'jkt': 'i4hY9y4DQNRuIIuFZ5blS5Fbze9v6rz88HlEi46Amnc'}\n" ] } ], @@ -362,7 +361,7 @@ }, { "cell_type": "markdown", - "id": "85a9a23a", + "id": "da9b4ad5", "metadata": {}, "source": [ "A replayed proof must be rejected — the `jti` ledger is atomic (ODIS's replay concern\n", @@ -372,13 +371,13 @@ { "cell_type": "code", "execution_count": 7, - "id": "bf17a225", + "id": "674bb840", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T01:44:44.138723Z", - "iopub.status.busy": "2026-09-04T01:44:44.138645Z", - "iopub.status.idle": "2026-09-04T01:44:44.143704Z", - "shell.execute_reply": "2026-09-04T01:44:44.143313Z" + "iopub.execute_input": "2026-09-04T17:59:07.641516Z", + "iopub.status.busy": "2026-09-04T17:59:07.641437Z", + "iopub.status.idle": "2026-09-04T17:59:07.646852Z", + "shell.execute_reply": "2026-09-04T17:59:07.646384Z" } }, "outputs": [ @@ -403,30 +402,31 @@ }, { "cell_type": "markdown", - "id": "8e0e0613", + "id": "7d6aa6c9", "metadata": {}, "source": [ "## 6 · Delegation Record — Layer 2 · The Bridge (ODIS §6.3, Pillar 4, L2-01/05/06)\n", "\n", "The orchestrator delegates to a sub-agent via **RFC 8693 token exchange**. The sub-agent\n", "proves possession of its own holder key (`actor_token`), and the granted scope is the\n", - "**three-way intersection**: requested ∩ what the orchestrator holds ∩ the sub-agent's policy\n", - "ceiling. The result carries ODIS Delegation-Record semantics as claims: `act` (the delegating\n", - "principal), `delegation_depth` (monotonic, capped by policy), and `mission_id` (chain lineage,\n", - "inherited from the root credential). Child expiry is clamped to the parent's — a child cannot\n", - "outlive its parent." + "**three-way intersection**: requested ∩ what the orchestrator holds ∩ the sub-agent's\n", + "**policy ceiling** — here a second `CredentialPolicy` whose `allowed_scopes` is just\n", + "`data:read`. The result carries ODIS Delegation-Record semantics as claims: `act` (the\n", + "delegating principal), `delegation_depth` (monotonic, capped by policy), and `mission_id`\n", + "(chain lineage, inherited from the root credential). Child expiry is clamped to the\n", + "parent's — a child cannot outlive its parent." ] }, { "cell_type": "code", "execution_count": 8, - "id": "d585fde0", + "id": "6006bca2", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T01:44:44.144908Z", - "iopub.status.busy": "2026-09-04T01:44:44.144823Z", - "iopub.status.idle": "2026-09-04T01:44:44.164297Z", - "shell.execute_reply": "2026-09-04T01:44:44.163871Z" + "iopub.execute_input": "2026-09-04T17:59:07.648013Z", + "iopub.status.busy": "2026-09-04T17:59:07.647919Z", + "iopub.status.idle": "2026-09-04T17:59:07.671346Z", + "shell.execute_reply": "2026-09-04T17:59:07.670911Z" } }, "outputs": [ @@ -435,24 +435,30 @@ "output_type": "stream", "text": [ "{\n", - " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/researcher-e54982\",\n", + " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/researcher-6a49f5\",\n", " \"act\": {\n", - " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-e54982\"\n", + " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-6a49f5\"\n", " },\n", " \"delegation_depth\": 1,\n", - " \"mission_id\": \"2199e5ed-da96-4b07-a27a-9b124cbfa220\",\n", - " \"jti\": \"faa546a8-8ee0-4a77-a9ed-7ad801748d49\"\n", + " \"mission_id\": \"a17223c6-885f-4258-ba21-3d579a78d348\",\n", + " \"jti\": \"61b0626f-bfea-4516-bbd5-624fda69b666\"\n", "}\n" ] } ], "source": [ + "researcher_policy = api(\"POST\", \"/credential-policies\", json={\n", + " \"name\": f\"odis-demo-researcher-{run}\", \"max_ttl_seconds\": 3600,\n", + " \"allowed_grant_types\": [\"api_key\", \"token_exchange\", \"client_credentials\"],\n", + " \"allowed_scopes\": [\"data:read\"], # the delegation ceiling\n", + " \"required_trust_level\": \"first_party\", \"max_delegation_depth\": 2,\n", + "}).json()\n", + "\n", "sub_key, sub_pem, _ = gen_key()\n", "r = api(\"POST\", \"/agents/register\", json={\n", " \"name\": \"Researcher\", \"external_id\": f\"researcher-{run}\", \"identity_type\": \"agent\",\n", - " \"sub_type\": \"tool_agent\", \"allowed_scopes\": [\"data:read\"],\n", - " \"created_by\": \"demo-admin@example.com\", \"public_key_pem\": sub_pem,\n", - " \"credential_policy_id\": policy[\"id\"],\n", + " \"sub_type\": \"tool_agent\", \"created_by\": \"demo-admin@example.com\",\n", + " \"public_key_pem\": sub_pem, \"credential_policy_id\": researcher_policy[\"id\"],\n", "})\n", "researcher = r.json()[\"identity\"]\n", "att2 = api(\"POST\", \"/attestation/submit\", json={\"identity_id\": researcher[\"id\"], \"level\": \"hardware\",\n", @@ -470,25 +476,26 @@ }, { "cell_type": "markdown", - "id": "7e036ab2", + "id": "0d04cc92", "metadata": {}, "source": [ - "Attenuation is monotonic and fails closed: the researcher's ceiling is `data:read`,\n", - "so a delegation that tries to smuggle `data:write` (which the *orchestrator* holds) is\n", - "refused — a sub-agent can never become a privilege-amplification point (ODIS-L2-06's core\n", - "demand):" + "Attenuation is **monotonic**: the researcher's policy ceiling is `data:read`, so a\n", + "delegation that tries to smuggle `data:write` (which the *orchestrator* holds) is narrowed\n", + "to the intersection — the child never receives more than its ceiling allows. And when\n", + "*nothing* survives the intersection, the exchange is refused outright. Either way a\n", + "sub-agent can never become a privilege-amplification point (ODIS-L2-06's core demand):" ] }, { "cell_type": "code", "execution_count": 9, - "id": "3dcfc549", + "id": "c1b27913", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T01:44:44.165338Z", - "iopub.status.busy": "2026-09-04T01:44:44.165261Z", - "iopub.status.idle": "2026-09-04T01:44:44.170246Z", - "shell.execute_reply": "2026-09-04T01:44:44.169822Z" + "iopub.execute_input": "2026-09-04T17:59:07.672340Z", + "iopub.status.busy": "2026-09-04T17:59:07.672253Z", + "iopub.status.idle": "2026-09-04T17:59:07.681506Z", + "shell.execute_reply": "2026-09-04T17:59:07.681073Z" } }, "outputs": [ @@ -496,10 +503,11 @@ "name": "stdout", "output_type": "stream", "text": [ + "requested: data:read data:write → granted: ['data:read']\n", "400\n", "{\n", - " \"error\": \"insufficient_scope\",\n", - " \"error_description\": \"one or more requested scopes are not permitted for this identity: \\\"data:write\\\" not in allowed_scopes\"\n", + " \"error\": \"invalid_scope\",\n", + " \"error_description\": \"requested scopes are not available for delegation\"\n", "}\n" ] } @@ -508,13 +516,20 @@ "r = requests.post(BASE + \"/oauth2/token\", json={\n", " \"grant_type\": \"urn:ietf:params:oauth:grant-type:token-exchange\",\n", " \"subject_token\": orch_token, \"actor_token\": actor_token, \"scope\": \"data:read data:write\"})\n", + "granted = claims_of(r.json()[\"access_token\"])[\"scopes\"]\n", + "print(\"requested: data:read data:write → granted:\", granted)\n", + "assert granted == [\"data:read\"]\n", + "\n", + "r = requests.post(BASE + \"/oauth2/token\", json={\n", + " \"grant_type\": \"urn:ietf:params:oauth:grant-type:token-exchange\",\n", + " \"subject_token\": orch_token, \"actor_token\": actor_token, \"scope\": \"data:write\"})\n", "print(r.status_code); show(r.json())\n", "assert r.status_code == 400" ] }, { "cell_type": "markdown", - "id": "f8a1a30f", + "id": "f19e3df8", "metadata": {}, "source": [ "## 7 · Bounded asynchronous human authorization — Layer 2 · The Bridge (ODIS-L2-02, Pillar 1)\n", @@ -537,13 +552,13 @@ { "cell_type": "code", "execution_count": 10, - "id": "305b0030", + "id": "d3317c61", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T01:44:44.171262Z", - "iopub.status.busy": "2026-09-04T01:44:44.171187Z", - "iopub.status.idle": "2026-09-04T01:44:44.487575Z", - "shell.execute_reply": "2026-09-04T01:44:44.486715Z" + "iopub.execute_input": "2026-09-04T17:59:07.682543Z", + "iopub.status.busy": "2026-09-04T17:59:07.682461Z", + "iopub.status.idle": "2026-09-04T17:59:08.001697Z", + "shell.execute_reply": "2026-09-04T17:59:08.000704Z" } }, "outputs": [ @@ -553,7 +568,7 @@ "text": [ "bc-authorize: 200\n", "{\n", - " \"auth_req_id\": \"dUSvAUpFS9bZOBaUKdH4dgjua9TgW90mBfNSs-Nty8k\",\n", + " \"auth_req_id\": \"PFKVVrG8DqX-BhLcm7FShhdovxynz8oDGoTbZ0n9fK4\",\n", " \"expires_in\": 300,\n", " \"interval\": 5\n", "}\n" @@ -567,7 +582,10 @@ "after approval — expires_in: 900 s\n", "{\n", " \"sub\": \"user-alice-001\",\n", - " \"token_exchange\": \"ciba\"\n", + " \"token_exchange\": \"ciba\",\n", + " \"scopes\": [\n", + " \"data:read\"\n", + " ]\n", "}\n" ] } @@ -599,12 +617,12 @@ "ciba_token = poll().json()\n", "cc2 = claims_of(ciba_token[\"access_token\"])\n", "print(\"after approval — expires_in:\", ciba_token[\"expires_in\"], \"s\")\n", - "show({k: cc2.get(k) for k in (\"sub\", \"token_exchange\", \"scope\") if cc2.get(k)})" + "show({k: cc2[k] for k in (\"sub\", \"token_exchange\", \"scopes\")})" ] }, { "cell_type": "markdown", - "id": "04b6379a", + "id": "3000f179", "metadata": {}, "source": [ "## 8 · Confirmed compromise → cascade revocation — ODIS-L1-12, L3-04 / L3-05\n", @@ -618,13 +636,13 @@ { "cell_type": "code", "execution_count": 11, - "id": "e005e54e", + "id": "82ecdb8b", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T01:44:44.489937Z", - "iopub.status.busy": "2026-09-04T01:44:44.489740Z", - "iopub.status.idle": "2026-09-04T01:44:44.502401Z", - "shell.execute_reply": "2026-09-04T01:44:44.501797Z" + "iopub.execute_input": "2026-09-04T17:59:08.004427Z", + "iopub.status.busy": "2026-09-04T17:59:08.004213Z", + "iopub.status.idle": "2026-09-04T17:59:08.018482Z", + "shell.execute_reply": "2026-09-04T17:59:08.017912Z" } }, "outputs": [ @@ -632,7 +650,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "delegated token after cascade: {'active': False} (9 ms signal→dead)\n" + "delegated token after cascade: {'active': False} (10 ms signal→dead)\n" ] } ], @@ -648,7 +666,7 @@ }, { "cell_type": "markdown", - "id": "5357845e", + "id": "300e06d2", "metadata": {}, "source": [ "## 9 · Audit lineage survives the kill — ODIS-CC-01 / CC-02\n", @@ -661,13 +679,13 @@ { "cell_type": "code", "execution_count": 12, - "id": "a8cb1217", + "id": "ebff77fa", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T01:44:44.503774Z", - "iopub.status.busy": "2026-09-04T01:44:44.503608Z", - "iopub.status.idle": "2026-09-04T01:44:44.510696Z", - "shell.execute_reply": "2026-09-04T01:44:44.510047Z" + "iopub.execute_input": "2026-09-04T17:59:08.019998Z", + "iopub.status.busy": "2026-09-04T17:59:08.019859Z", + "iopub.status.idle": "2026-09-04T17:59:08.026758Z", + "shell.execute_reply": "2026-09-04T17:59:08.026287Z" } }, "outputs": [ @@ -675,7 +693,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "chain: orch-e54982 → researcher-e54982\n", + "chain: orch-6a49f5 → researcher-6a49f5\n", "[\n", " {\n", " \"grant_type\": \"api_key\",\n", @@ -690,7 +708,7 @@ " ],\n", " \"attenuated\": [],\n", " \"is_revoked\": true,\n", - " \"revoke_reason\": \"auto-revoked by CAE signal 5029913d-fd6b-46fa-aa48-57f4ee7c6393 (severity: critical)\"\n", + " \"revoke_reason\": \"auto-revoked by CAE signal 362cdff4-9f2e-497a-940b-741f45a26a2d (severity: critical)\"\n", " },\n", " {\n", " \"grant_type\": \"token_exchange\",\n", @@ -706,7 +724,7 @@ " \"data:write\"\n", " ],\n", " \"is_revoked\": true,\n", - " \"revoke_reason\": \"auto-revoked by CAE signal 5029913d-fd6b-46fa-aa48-57f4ee7c6393 (severity: critical)\"\n", + " \"revoke_reason\": \"auto-revoked by CAE signal 362cdff4-9f2e-497a-940b-741f45a26a2d (severity: critical)\"\n", " }\n", "]\n" ] @@ -721,7 +739,7 @@ }, { "cell_type": "markdown", - "id": "1378c136", + "id": "8dcf2460", "metadata": {}, "source": [ "## 10 · The sponsor leaves — offboarding kill switch (ODIS-L1-06 / L1-10 / L3-05)\n", @@ -742,13 +760,13 @@ { "cell_type": "code", "execution_count": 13, - "id": "64334023", + "id": "39ba4dcc", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T01:44:44.511983Z", - "iopub.status.busy": "2026-09-04T01:44:44.511882Z", - "iopub.status.idle": "2026-09-04T01:44:44.528914Z", - "shell.execute_reply": "2026-09-04T01:44:44.528351Z" + "iopub.execute_input": "2026-09-04T17:59:08.028139Z", + "iopub.status.busy": "2026-09-04T17:59:08.028031Z", + "iopub.status.idle": "2026-09-04T17:59:08.042928Z", + "shell.execute_reply": "2026-09-04T17:59:08.042543Z" } }, "outputs": [ @@ -761,7 +779,13 @@ " \"identities_deactivated\": 1,\n", " \"credentials_revoked\": 0\n", "}\n", - "alice's CIBA-approved token: {'active': False}\n", + "alice's CIBA-approved token: {'active': False}\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ "[\n", " {\n", " \"grant_type\": \"urn:openid:params:grant-type:ciba\",\n", @@ -786,7 +810,7 @@ }, { "cell_type": "markdown", - "id": "966c79af", + "id": "b531752d", "metadata": {}, "source": [ "## What you just saw, in ODIS terms\n", @@ -798,7 +822,7 @@ "| L2-14 registration resolution before authority; L1-11 fail closed | The Bridge | §3 — issuance refused for an unattested identity |\n", "| L1-03 / L1-11 attestation-bootstrapped trust | The Passport | §4 — verification promoted trust, atomically |\n", "| §6.2 Agent Runtime Credential; L1-05 / L1-09 proof-of-possession | The Passport | §5 — DPoP-bound token, replay rejected (`token.require_dpop` makes binding mandatory deployment-wide) |\n", - "| §6.3 Delegation Record; Pillar 4; L2-01 / L2-05 / L2-06 monotonic attenuation | The Bridge | §6 — `act` chain, depth, escalation refused |\n", + "| §6.3 Delegation Record; Pillar 4; L2-01 / L2-05 / L2-06 monotonic attenuation | The Bridge | §6 — `act` chain, depth; out-of-ceiling scope narrowed, empty intersection refused |\n", "| L2-02 bounded async authorization; Pillar 1 delegated principal identity | The Bridge | §7 — CIBA approval with binding message; token `sub` = the human |\n", "| L1-12 / L3-04 / L3-05 compromise signal, cascade | (Router-adjacent) | §8 — critical signal killed the whole tree in milliseconds |\n", "| CC-01 / CC-02 dual-identity audit lineage | cross-cutting | §9 — the graph outlives the credentials |\n", From 09e7a049b087081607244be345bf34b8fb33b4e4 Mon Sep 17 00:00:00 2001 From: Sharath Rajasekar Date: Fri, 4 Sep 2026 13:31:08 -0700 Subject: [PATCH 10/14] docs: re-execute ODIS notebooks against main after #319; re-pin the statement's evidence commit Co-Authored-By: Claude Fable 5 --- docs/odis/role-capability-statement.md | 2 +- examples/odis/odis-walkthrough-sdk.ipynb | 106 ++++++------ examples/odis/odis-walkthrough.ipynb | 209 ++++++++++++----------- 3 files changed, 159 insertions(+), 158 deletions(-) diff --git a/docs/odis/role-capability-statement.md b/docs/odis/role-capability-statement.md index ef0a7197..713e0acb 100644 --- a/docs/odis/role-capability-statement.md +++ b/docs/odis/role-capability-statement.md @@ -2,7 +2,7 @@ **Status**: Draft for submission to the CoSAI/OASIS WS4 ODIS workstream **ODIS reference**: [ODIS.md draft](https://github.com/cosai-oasis/ws4-odis/blob/main/RFCs/ODIS.md) (unapproved contributor draft) -**ZeroID version**: commit `d2531cd` on `main` (PR #304's `token.require_dpop`, attestation trust-clamp, and CIBA credential anchoring; PR #318's root-mounted admin API; Apache-2.0, ) +**ZeroID version**: commit `054a2dc` on `main` (PR #304's `token.require_dpop`, attestation trust-clamp, and CIBA credential anchoring; PR #318's root-mounted admin API and PR #319's hardening of it; Apache-2.0, ) **Claim type**: role-capability statement per ODIS §8. **This is not an ODIS Core, Extended, or Safety profile claim** — §8 reserves those for a complete conformance target, and this document itself identifies unmet MUSTs. ZeroID is an OAuth 2.1/OIDC authorization server for AI-agent identity, built diff --git a/examples/odis/odis-walkthrough-sdk.ipynb b/examples/odis/odis-walkthrough-sdk.ipynb index 7b6257e4..a17d56ef 100644 --- a/examples/odis/odis-walkthrough-sdk.ipynb +++ b/examples/odis/odis-walkthrough-sdk.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "62d21988", + "id": "9d0f7547", "metadata": {}, "source": [ "# ZeroID as ODIS Layers 1–2 — the SDK companion\n", @@ -38,13 +38,13 @@ { "cell_type": "code", "execution_count": 1, - "id": "9634ed4a", + "id": "d24269e6", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T17:59:24.452141Z", - "iopub.status.busy": "2026-09-04T17:59:24.452035Z", - "iopub.status.idle": "2026-09-04T17:59:24.573608Z", - "shell.execute_reply": "2026-09-04T17:59:24.573249Z" + "iopub.execute_input": "2026-09-04T20:30:53.608113Z", + "iopub.status.busy": "2026-09-04T20:30:53.607777Z", + "iopub.status.idle": "2026-09-04T20:30:53.751586Z", + "shell.execute_reply": "2026-09-04T20:30:53.751087Z" } }, "outputs": [ @@ -52,7 +52,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "status='healthy' service='zeroid' timestamp=datetime.datetime(2026, 9, 4, 17, 59, 24, 570241, tzinfo=TzInfo(0)) uptime_ms=94152 | run id: 088aef\n" + "status='healthy' service='zeroid' timestamp=datetime.datetime(2026, 9, 4, 20, 30, 53, 747115, tzinfo=TzInfo(0)) uptime_ms=19623 | run id: 235971\n" ] } ], @@ -70,7 +70,7 @@ }, { "cell_type": "markdown", - "id": "8b9f2400", + "id": "00944b05", "metadata": {}, "source": [ "## 1 · Agent Registration Record — Layer 1 · The Passport (ODIS §6.1)\n", @@ -86,13 +86,13 @@ { "cell_type": "code", "execution_count": 2, - "id": "bef5847b", + "id": "9564dc00", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T17:59:24.575019Z", - "iopub.status.busy": "2026-09-04T17:59:24.574922Z", - "iopub.status.idle": "2026-09-04T17:59:24.586734Z", - "shell.execute_reply": "2026-09-04T17:59:24.586362Z" + "iopub.execute_input": "2026-09-04T20:30:53.752778Z", + "iopub.status.busy": "2026-09-04T20:30:53.752671Z", + "iopub.status.idle": "2026-09-04T20:30:53.761587Z", + "shell.execute_reply": "2026-09-04T20:30:53.761094Z" } }, "outputs": [ @@ -100,7 +100,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-088aef | trust: unverified | status: active\n" + "spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-235971 | trust: unverified | status: active\n" ] } ], @@ -114,7 +114,7 @@ }, { "cell_type": "markdown", - "id": "9707cd70", + "id": "9f6328dd", "metadata": {}, "source": [ "## 2 · Agent Runtime Credential — the Passport's output (ODIS §6.2, L1-05)\n", @@ -126,13 +126,13 @@ { "cell_type": "code", "execution_count": 3, - "id": "3bdd6999", + "id": "7f3fd466", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T17:59:24.587881Z", - "iopub.status.busy": "2026-09-04T17:59:24.587810Z", - "iopub.status.idle": "2026-09-04T17:59:24.595667Z", - "shell.execute_reply": "2026-09-04T17:59:24.595235Z" + "iopub.execute_input": "2026-09-04T20:30:53.762746Z", + "iopub.status.busy": "2026-09-04T20:30:53.762667Z", + "iopub.status.idle": "2026-09-04T20:30:53.771596Z", + "shell.execute_reply": "2026-09-04T20:30:53.771116Z" } }, "outputs": [ @@ -151,7 +151,7 @@ }, { "cell_type": "markdown", - "id": "5e0526ad", + "id": "0e3ed41b", "metadata": {}, "source": [ "## 3 · The ODIS-aware target — the Bridge's native mode (ODIS §2.5 / L2-15)\n", @@ -176,13 +176,13 @@ { "cell_type": "code", "execution_count": 4, - "id": "ba9787c8", + "id": "f4b34892", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T17:59:24.596869Z", - "iopub.status.busy": "2026-09-04T17:59:24.596788Z", - "iopub.status.idle": "2026-09-04T17:59:24.602785Z", - "shell.execute_reply": "2026-09-04T17:59:24.602391Z" + "iopub.execute_input": "2026-09-04T20:30:53.772739Z", + "iopub.status.busy": "2026-09-04T20:30:53.772646Z", + "iopub.status.idle": "2026-09-04T20:30:53.779843Z", + "shell.execute_reply": "2026-09-04T20:30:53.779393Z" } }, "outputs": [ @@ -190,7 +190,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "verified: spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-088aef | delegated: False\n", + "verified: spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-235971 | delegated: False\n", "refused (ZeroIDError): Missing required scope: 'admin:all'\n", "refused (ZeroIDError): Insufficient trust level: required 'first_party', got 'unverified'\n" ] @@ -211,7 +211,7 @@ }, { "cell_type": "markdown", - "id": "e8de1b59", + "id": "2dd70fbd", "metadata": {}, "source": [ "Audience is the first duty on ODIS §2.5's native-mode list, and it is one argument\n", @@ -223,13 +223,13 @@ { "cell_type": "code", "execution_count": 5, - "id": "a3195e2d", + "id": "1cf01465", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T17:59:24.603925Z", - "iopub.status.busy": "2026-09-04T17:59:24.603836Z", - "iopub.status.idle": "2026-09-04T17:59:24.606521Z", - "shell.execute_reply": "2026-09-04T17:59:24.606123Z" + "iopub.execute_input": "2026-09-04T20:30:53.780991Z", + "iopub.status.busy": "2026-09-04T20:30:53.780898Z", + "iopub.status.idle": "2026-09-04T20:30:53.783581Z", + "shell.execute_reply": "2026-09-04T20:30:53.783191Z" } }, "outputs": [ @@ -254,7 +254,7 @@ }, { "cell_type": "markdown", - "id": "d71077de", + "id": "480da406", "metadata": {}, "source": [ "Trust is **minted into the credential at issuance** — `trust_level` is a claim. So\n", @@ -270,13 +270,13 @@ { "cell_type": "code", "execution_count": 6, - "id": "034f887d", + "id": "a01f99ae", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T17:59:24.607659Z", - "iopub.status.busy": "2026-09-04T17:59:24.607581Z", - "iopub.status.idle": "2026-09-04T17:59:24.618392Z", - "shell.execute_reply": "2026-09-04T17:59:24.617915Z" + "iopub.execute_input": "2026-09-04T20:30:53.784886Z", + "iopub.status.busy": "2026-09-04T20:30:53.784807Z", + "iopub.status.idle": "2026-09-04T20:30:53.796512Z", + "shell.execute_reply": "2026-09-04T20:30:53.795962Z" } }, "outputs": [ @@ -298,7 +298,7 @@ }, { "cell_type": "markdown", - "id": "14d1dadf", + "id": "8de6c9ee", "metadata": {}, "source": [ "## 4 · Delegation Record — Layer 2 · The Bridge (ODIS §6.3, Pillar 4, L2-05/06)\n", @@ -315,13 +315,13 @@ { "cell_type": "code", "execution_count": 7, - "id": "8294fa35", + "id": "77ebcec6", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T17:59:24.619443Z", - "iopub.status.busy": "2026-09-04T17:59:24.619361Z", - "iopub.status.idle": "2026-09-04T17:59:24.636609Z", - "shell.execute_reply": "2026-09-04T17:59:24.636074Z" + "iopub.execute_input": "2026-09-04T20:30:53.797607Z", + "iopub.status.busy": "2026-09-04T20:30:53.797526Z", + "iopub.status.idle": "2026-09-04T20:30:53.816167Z", + "shell.execute_reply": "2026-09-04T20:30:53.815438Z" } }, "outputs": [ @@ -330,7 +330,7 @@ "output_type": "stream", "text": [ "granted scope (requested read+write): 'data:read'\n", - "sub: researcher-088aef | is_delegated: True | act: {'sub': 'spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-088aef'} | depth: 1\n", + "sub: researcher-235971 | is_delegated: True | act: {'sub': 'spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-235971'} | depth: 1\n", "empty intersection refused (APIError): [400] invalid_scope: requested scopes are not available for delegation\n" ] } @@ -370,7 +370,7 @@ }, { "cell_type": "markdown", - "id": "a8e646cf", + "id": "3b20e461", "metadata": {}, "source": [ "## 5 · Compromise signal, cascade — and what native mode must add — L1-12, L3-04/05\n", @@ -387,13 +387,13 @@ { "cell_type": "code", "execution_count": 8, - "id": "fa65e76c", + "id": "a461850c", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T17:59:24.637723Z", - "iopub.status.busy": "2026-09-04T17:59:24.637635Z", - "iopub.status.idle": "2026-09-04T17:59:24.644378Z", - "shell.execute_reply": "2026-09-04T17:59:24.643955Z" + "iopub.execute_input": "2026-09-04T20:30:53.817663Z", + "iopub.status.busy": "2026-09-04T20:30:53.817540Z", + "iopub.status.idle": "2026-09-04T20:30:53.825474Z", + "shell.execute_reply": "2026-09-04T20:30:53.824977Z" } }, "outputs": [ @@ -418,7 +418,7 @@ }, { "cell_type": "markdown", - "id": "8233ffe9", + "id": "353c5040", "metadata": {}, "source": [ "## What the SDK view added\n", diff --git a/examples/odis/odis-walkthrough.ipynb b/examples/odis/odis-walkthrough.ipynb index f70a9e5c..7f07d498 100644 --- a/examples/odis/odis-walkthrough.ipynb +++ b/examples/odis/odis-walkthrough.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "60ad7e36", + "id": "f0af314d", "metadata": {}, "source": [ "# ZeroID as ODIS Layers 1–2 — an executable walkthrough\n", @@ -48,13 +48,13 @@ { "cell_type": "code", "execution_count": 1, - "id": "461baa10", + "id": "73e74ab7", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T17:59:06.503146Z", - "iopub.status.busy": "2026-09-04T17:59:06.502960Z", - "iopub.status.idle": "2026-09-04T17:59:07.553629Z", - "shell.execute_reply": "2026-09-04T17:59:07.552942Z" + "iopub.execute_input": "2026-09-04T20:30:52.268795Z", + "iopub.status.busy": "2026-09-04T20:30:52.268507Z", + "iopub.status.idle": "2026-09-04T20:30:52.339785Z", + "shell.execute_reply": "2026-09-04T20:30:52.339289Z" } }, "outputs": [ @@ -62,7 +62,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "zeroid: healthy | run id: 6a49f5\n" + "zeroid: healthy | run id: e77a4d\n" ] } ], @@ -100,7 +100,7 @@ }, { "cell_type": "markdown", - "id": "8d0669ea", + "id": "4e50a083", "metadata": {}, "source": [ "## 1 · Agent Registration Record — Layer 1 · The Passport (ODIS §6.1)\n", @@ -115,13 +115,13 @@ { "cell_type": "code", "execution_count": 2, - "id": "574ada48", + "id": "d5bd8c0d", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T17:59:07.555670Z", - "iopub.status.busy": "2026-09-04T17:59:07.555493Z", - "iopub.status.idle": "2026-09-04T17:59:07.580263Z", - "shell.execute_reply": "2026-09-04T17:59:07.579681Z" + "iopub.execute_input": "2026-09-04T20:30:52.341125Z", + "iopub.status.busy": "2026-09-04T20:30:52.341015Z", + "iopub.status.idle": "2026-09-04T20:30:52.369522Z", + "shell.execute_reply": "2026-09-04T20:30:52.369088Z" } }, "outputs": [ @@ -130,7 +130,7 @@ "output_type": "stream", "text": [ "{\n", - " \"wimse_uri\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-6a49f5\",\n", + " \"wimse_uri\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-e77a4d\",\n", " \"identity_type\": \"agent\",\n", " \"trust_level\": \"unverified\",\n", " \"status\": \"active\",\n", @@ -153,7 +153,7 @@ }, { "cell_type": "markdown", - "id": "1111f5c6", + "id": "2421b53d", "metadata": {}, "source": [ "## 2 · Registration-record governance fields — Layer 1 · The Passport (ODIS §6.1)\n", @@ -169,13 +169,13 @@ { "cell_type": "code", "execution_count": 3, - "id": "68fbce6e", + "id": "2b59471c", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T17:59:07.581532Z", - "iopub.status.busy": "2026-09-04T17:59:07.581430Z", - "iopub.status.idle": "2026-09-04T17:59:07.594817Z", - "shell.execute_reply": "2026-09-04T17:59:07.594245Z" + "iopub.execute_input": "2026-09-04T20:30:52.370890Z", + "iopub.status.busy": "2026-09-04T20:30:52.370811Z", + "iopub.status.idle": "2026-09-04T20:30:52.383949Z", + "shell.execute_reply": "2026-09-04T20:30:52.383512Z" } }, "outputs": [ @@ -184,7 +184,7 @@ "output_type": "stream", "text": [ "{\n", - " \"name\": \"odis-demo-6a49f5\",\n", + " \"name\": \"odis-demo-e77a4d\",\n", " \"max_ttl_seconds\": 3600,\n", " \"required_trust_level\": \"first_party\",\n", " \"max_delegation_depth\": 2\n", @@ -208,7 +208,7 @@ }, { "cell_type": "markdown", - "id": "36ed3c27", + "id": "7c01ce5d", "metadata": {}, "source": [ "## 3 · No registration resolution, no authority — The Bridge refuses (ODIS-L2-14 + L1-11)\n", @@ -221,13 +221,13 @@ { "cell_type": "code", "execution_count": 4, - "id": "971b2ce6", + "id": "30384af4", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T17:59:07.596144Z", - "iopub.status.busy": "2026-09-04T17:59:07.596014Z", - "iopub.status.idle": "2026-09-04T17:59:07.604540Z", - "shell.execute_reply": "2026-09-04T17:59:07.603986Z" + "iopub.execute_input": "2026-09-04T20:30:52.385300Z", + "iopub.status.busy": "2026-09-04T20:30:52.385207Z", + "iopub.status.idle": "2026-09-04T20:30:52.393810Z", + "shell.execute_reply": "2026-09-04T20:30:52.393357Z" } }, "outputs": [ @@ -251,7 +251,7 @@ }, { "cell_type": "markdown", - "id": "1fe42210", + "id": "21062c6f", "metadata": {}, "source": [ "## 4 · Attestation raises trust — Layer 1 · The Passport (ODIS-L1-03 / L1-11)\n", @@ -268,13 +268,13 @@ { "cell_type": "code", "execution_count": 5, - "id": "e350aa0b", + "id": "cfc45869", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T17:59:07.605714Z", - "iopub.status.busy": "2026-09-04T17:59:07.605629Z", - "iopub.status.idle": "2026-09-04T17:59:07.623717Z", - "shell.execute_reply": "2026-09-04T17:59:07.623252Z" + "iopub.execute_input": "2026-09-04T20:30:52.395100Z", + "iopub.status.busy": "2026-09-04T20:30:52.395029Z", + "iopub.status.idle": "2026-09-04T20:30:52.412485Z", + "shell.execute_reply": "2026-09-04T20:30:52.412037Z" } }, "outputs": [ @@ -299,7 +299,7 @@ }, { "cell_type": "markdown", - "id": "4d747fcd", + "id": "194e33d3", "metadata": {}, "source": [ "## 5 · Agent Runtime Credential — the Passport's output (ODIS §6.2, L1-05 / L1-09)\n", @@ -320,13 +320,13 @@ { "cell_type": "code", "execution_count": 6, - "id": "e138d67d", + "id": "3db7ab5c", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T17:59:07.625240Z", - "iopub.status.busy": "2026-09-04T17:59:07.625141Z", - "iopub.status.idle": "2026-09-04T17:59:07.640374Z", - "shell.execute_reply": "2026-09-04T17:59:07.639903Z" + "iopub.execute_input": "2026-09-04T20:30:52.413690Z", + "iopub.status.busy": "2026-09-04T20:30:52.413608Z", + "iopub.status.idle": "2026-09-04T20:30:52.429968Z", + "shell.execute_reply": "2026-09-04T20:30:52.429492Z" } }, "outputs": [ @@ -336,12 +336,12 @@ "text": [ "token_type: DPoP | expires_in: 3600 s\n", "{\n", - " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-6a49f5\",\n", + " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-e77a4d\",\n", " \"trust_level\": \"first_party\",\n", - " \"mission_id\": \"a17223c6-885f-4258-ba21-3d579a78d348\",\n", - " \"jti\": \"a17223c6-885f-4258-ba21-3d579a78d348\"\n", + " \"mission_id\": \"b8f71064-cebc-479b-8c27-ab61ba2f3e62\",\n", + " \"jti\": \"b8f71064-cebc-479b-8c27-ab61ba2f3e62\"\n", "}\n", - "cnf: {'jkt': 'i4hY9y4DQNRuIIuFZ5blS5Fbze9v6rz88HlEi46Amnc'}\n" + "cnf: {'jkt': 'g5qlBflyMag7ECGtBO66ZFOs33AKSAmIUxiVzs9S-Cw'}\n" ] } ], @@ -361,7 +361,7 @@ }, { "cell_type": "markdown", - "id": "da9b4ad5", + "id": "f1ca8262", "metadata": {}, "source": [ "A replayed proof must be rejected — the `jti` ledger is atomic (ODIS's replay concern\n", @@ -371,13 +371,13 @@ { "cell_type": "code", "execution_count": 7, - "id": "674bb840", + "id": "52b1234c", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T17:59:07.641516Z", - "iopub.status.busy": "2026-09-04T17:59:07.641437Z", - "iopub.status.idle": "2026-09-04T17:59:07.646852Z", - "shell.execute_reply": "2026-09-04T17:59:07.646384Z" + "iopub.execute_input": "2026-09-04T20:30:52.431046Z", + "iopub.status.busy": "2026-09-04T20:30:52.430954Z", + "iopub.status.idle": "2026-09-04T20:30:52.435977Z", + "shell.execute_reply": "2026-09-04T20:30:52.435567Z" } }, "outputs": [ @@ -402,7 +402,7 @@ }, { "cell_type": "markdown", - "id": "7d6aa6c9", + "id": "736cbbc9", "metadata": {}, "source": [ "## 6 · Delegation Record — Layer 2 · The Bridge (ODIS §6.3, Pillar 4, L2-01/05/06)\n", @@ -420,13 +420,13 @@ { "cell_type": "code", "execution_count": 8, - "id": "6006bca2", + "id": "1557c62f", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T17:59:07.648013Z", - "iopub.status.busy": "2026-09-04T17:59:07.647919Z", - "iopub.status.idle": "2026-09-04T17:59:07.671346Z", - "shell.execute_reply": "2026-09-04T17:59:07.670911Z" + "iopub.execute_input": "2026-09-04T20:30:52.437020Z", + "iopub.status.busy": "2026-09-04T20:30:52.436942Z", + "iopub.status.idle": "2026-09-04T20:30:52.460554Z", + "shell.execute_reply": "2026-09-04T20:30:52.460061Z" } }, "outputs": [ @@ -435,13 +435,13 @@ "output_type": "stream", "text": [ "{\n", - " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/researcher-6a49f5\",\n", + " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/researcher-e77a4d\",\n", " \"act\": {\n", - " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-6a49f5\"\n", + " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-e77a4d\"\n", " },\n", " \"delegation_depth\": 1,\n", - " \"mission_id\": \"a17223c6-885f-4258-ba21-3d579a78d348\",\n", - " \"jti\": \"61b0626f-bfea-4516-bbd5-624fda69b666\"\n", + " \"mission_id\": \"b8f71064-cebc-479b-8c27-ab61ba2f3e62\",\n", + " \"jti\": \"72c1ce23-188d-4e14-a93b-eaa0235750ce\"\n", "}\n" ] } @@ -476,7 +476,7 @@ }, { "cell_type": "markdown", - "id": "0d04cc92", + "id": "1c487403", "metadata": {}, "source": [ "Attenuation is **monotonic**: the researcher's policy ceiling is `data:read`, so a\n", @@ -489,13 +489,13 @@ { "cell_type": "code", "execution_count": 9, - "id": "c1b27913", + "id": "c4192f3f", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T17:59:07.672340Z", - "iopub.status.busy": "2026-09-04T17:59:07.672253Z", - "iopub.status.idle": "2026-09-04T17:59:07.681506Z", - "shell.execute_reply": "2026-09-04T17:59:07.681073Z" + "iopub.execute_input": "2026-09-04T20:30:52.461621Z", + "iopub.status.busy": "2026-09-04T20:30:52.461539Z", + "iopub.status.idle": "2026-09-04T20:30:52.471041Z", + "shell.execute_reply": "2026-09-04T20:30:52.470645Z" } }, "outputs": [ @@ -529,7 +529,7 @@ }, { "cell_type": "markdown", - "id": "f19e3df8", + "id": "8a680649", "metadata": {}, "source": [ "## 7 · Bounded asynchronous human authorization — Layer 2 · The Bridge (ODIS-L2-02, Pillar 1)\n", @@ -552,13 +552,13 @@ { "cell_type": "code", "execution_count": 10, - "id": "d3317c61", + "id": "9dfc5520", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T17:59:07.682543Z", - "iopub.status.busy": "2026-09-04T17:59:07.682461Z", - "iopub.status.idle": "2026-09-04T17:59:08.001697Z", - "shell.execute_reply": "2026-09-04T17:59:08.000704Z" + "iopub.execute_input": "2026-09-04T20:30:52.472153Z", + "iopub.status.busy": "2026-09-04T20:30:52.472076Z", + "iopub.status.idle": "2026-09-04T20:30:52.801324Z", + "shell.execute_reply": "2026-09-04T20:30:52.798208Z" } }, "outputs": [ @@ -566,9 +566,16 @@ "name": "stdout", "output_type": "stream", "text": [ - "bc-authorize: 200\n", + "bc-authorize:" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 200\n", "{\n", - " \"auth_req_id\": \"PFKVVrG8DqX-BhLcm7FShhdovxynz8oDGoTbZ0n9fK4\",\n", + " \"auth_req_id\": \"CJhAxkGirS5ZMc0KC2xZanLnYdhg7YAhXr71dCQ1U4U\",\n", " \"expires_in\": 300,\n", " \"interval\": 5\n", "}\n" @@ -622,7 +629,7 @@ }, { "cell_type": "markdown", - "id": "3000f179", + "id": "f965ca71", "metadata": {}, "source": [ "## 8 · Confirmed compromise → cascade revocation — ODIS-L1-12, L3-04 / L3-05\n", @@ -636,13 +643,13 @@ { "cell_type": "code", "execution_count": 11, - "id": "82ecdb8b", + "id": "e5bed532", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T17:59:08.004427Z", - "iopub.status.busy": "2026-09-04T17:59:08.004213Z", - "iopub.status.idle": "2026-09-04T17:59:08.018482Z", - "shell.execute_reply": "2026-09-04T17:59:08.017912Z" + "iopub.execute_input": "2026-09-04T20:30:52.805034Z", + "iopub.status.busy": "2026-09-04T20:30:52.804744Z", + "iopub.status.idle": "2026-09-04T20:30:52.825985Z", + "shell.execute_reply": "2026-09-04T20:30:52.824720Z" } }, "outputs": [ @@ -650,7 +657,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "delegated token after cascade: {'active': False} (10 ms signal→dead)\n" + "delegated token after cascade: {'active': False} (15 ms signal→dead)\n" ] } ], @@ -666,7 +673,7 @@ }, { "cell_type": "markdown", - "id": "300e06d2", + "id": "4feae2db", "metadata": {}, "source": [ "## 9 · Audit lineage survives the kill — ODIS-CC-01 / CC-02\n", @@ -679,13 +686,13 @@ { "cell_type": "code", "execution_count": 12, - "id": "ebff77fa", + "id": "41ff4b22", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T17:59:08.019998Z", - "iopub.status.busy": "2026-09-04T17:59:08.019859Z", - "iopub.status.idle": "2026-09-04T17:59:08.026758Z", - "shell.execute_reply": "2026-09-04T17:59:08.026287Z" + "iopub.execute_input": "2026-09-04T20:30:52.828624Z", + "iopub.status.busy": "2026-09-04T20:30:52.828407Z", + "iopub.status.idle": "2026-09-04T20:30:52.840067Z", + "shell.execute_reply": "2026-09-04T20:30:52.839274Z" } }, "outputs": [ @@ -693,7 +700,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "chain: orch-6a49f5 → researcher-6a49f5\n", + "chain: orch-e77a4d → researcher-e77a4d\n", "[\n", " {\n", " \"grant_type\": \"api_key\",\n", @@ -708,7 +715,7 @@ " ],\n", " \"attenuated\": [],\n", " \"is_revoked\": true,\n", - " \"revoke_reason\": \"auto-revoked by CAE signal 362cdff4-9f2e-497a-940b-741f45a26a2d (severity: critical)\"\n", + " \"revoke_reason\": \"auto-revoked by CAE signal 04b02137-8b78-40d1-b0a5-aeb9d9d2adda (severity: critical)\"\n", " },\n", " {\n", " \"grant_type\": \"token_exchange\",\n", @@ -724,7 +731,7 @@ " \"data:write\"\n", " ],\n", " \"is_revoked\": true,\n", - " \"revoke_reason\": \"auto-revoked by CAE signal 362cdff4-9f2e-497a-940b-741f45a26a2d (severity: critical)\"\n", + " \"revoke_reason\": \"auto-revoked by CAE signal 04b02137-8b78-40d1-b0a5-aeb9d9d2adda (severity: critical)\"\n", " }\n", "]\n" ] @@ -739,7 +746,7 @@ }, { "cell_type": "markdown", - "id": "8dcf2460", + "id": "c7a50761", "metadata": {}, "source": [ "## 10 · The sponsor leaves — offboarding kill switch (ODIS-L1-06 / L1-10 / L3-05)\n", @@ -760,13 +767,13 @@ { "cell_type": "code", "execution_count": 13, - "id": "39ba4dcc", + "id": "d8041dac", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T17:59:08.028139Z", - "iopub.status.busy": "2026-09-04T17:59:08.028031Z", - "iopub.status.idle": "2026-09-04T17:59:08.042928Z", - "shell.execute_reply": "2026-09-04T17:59:08.042543Z" + "iopub.execute_input": "2026-09-04T20:30:52.842783Z", + "iopub.status.busy": "2026-09-04T20:30:52.842556Z", + "iopub.status.idle": "2026-09-04T20:30:52.862157Z", + "shell.execute_reply": "2026-09-04T20:30:52.861445Z" } }, "outputs": [ @@ -775,17 +782,11 @@ "output_type": "stream", "text": [ "{\n", - " \"$schema\": \"http://localhost:8899/OffboardByOwnerOutputBody.json\",\n", + " \"$schema\": \"http://localhost:8899/admin/schemas/OffboardByOwnerOutputBody.json\",\n", " \"identities_deactivated\": 1,\n", " \"credentials_revoked\": 0\n", "}\n", - "alice's CIBA-approved token: {'active': False}\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ + "alice's CIBA-approved token: {'active': False}\n", "[\n", " {\n", " \"grant_type\": \"urn:openid:params:grant-type:ciba\",\n", @@ -810,7 +811,7 @@ }, { "cell_type": "markdown", - "id": "b531752d", + "id": "f8b13be7", "metadata": {}, "source": [ "## What you just saw, in ODIS terms\n", From 4c6d94cf2c66eb74eefa40f33496323eaea1c4a7 Mon Sep 17 00:00:00 2001 From: Sharath Rajasekar Date: Fri, 4 Sep 2026 13:32:50 -0700 Subject: [PATCH 11/14] docs: SDK notebook and README instruct pip install highflame (latest), recording the version the outputs came from Co-Authored-By: Claude Fable 5 --- examples/odis/README.md | 5 +- examples/odis/odis-walkthrough-sdk.ipynb | 108 +++++++++++------------ 2 files changed, 57 insertions(+), 56 deletions(-) diff --git a/examples/odis/README.md b/examples/odis/README.md index e0dc0cc9..dd750ab0 100644 --- a/examples/odis/README.md +++ b/examples/odis/README.md @@ -36,8 +36,9 @@ pip install requests pyjwt cryptography jupyter jupyter notebook examples/odis/odis-walkthrough.ipynb ``` -The SDK notebook additionally needs `pip install "highflame==0.3.23"` — the -version its committed outputs were generated against. +The SDK notebook additionally needs `pip install highflame` (latest — CI executes +it against the latest release on every PR; the committed outputs were generated with +highflame 0.3.23). ## Which configuration each notebook runs under diff --git a/examples/odis/odis-walkthrough-sdk.ipynb b/examples/odis/odis-walkthrough-sdk.ipynb index a17d56ef..3fb2e012 100644 --- a/examples/odis/odis-walkthrough-sdk.ipynb +++ b/examples/odis/odis-walkthrough-sdk.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "9d0f7547", + "id": "9b718945", "metadata": {}, "source": [ "# ZeroID as ODIS Layers 1–2 — the SDK companion\n", @@ -20,7 +20,7 @@ " tool server calls before executing anything.\n", "\n", "**Prerequisites** (repo root): `make setup-keys && docker compose up -d`, then\n", - "`pip install \"highflame==0.3.23\" pyjwt cryptography`.\n", + "`pip install highflame pyjwt cryptography` (the committed outputs were generated with highflame 0.3.23).\n", "\n", "> **Configuration note (ODIS-L1-09):** this notebook requires the compose default\n", "> `token.require_dpop: false`. The Python SDK cannot construct DPoP proofs yet\n", @@ -38,13 +38,13 @@ { "cell_type": "code", "execution_count": 1, - "id": "d24269e6", + "id": "76c87892", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:30:53.608113Z", - "iopub.status.busy": "2026-09-04T20:30:53.607777Z", - "iopub.status.idle": "2026-09-04T20:30:53.751586Z", - "shell.execute_reply": "2026-09-04T20:30:53.751087Z" + "iopub.execute_input": "2026-09-04T20:32:50.603225Z", + "iopub.status.busy": "2026-09-04T20:32:50.602994Z", + "iopub.status.idle": "2026-09-04T20:32:50.728239Z", + "shell.execute_reply": "2026-09-04T20:32:50.727710Z" } }, "outputs": [ @@ -52,7 +52,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "status='healthy' service='zeroid' timestamp=datetime.datetime(2026, 9, 4, 20, 30, 53, 747115, tzinfo=TzInfo(0)) uptime_ms=19623 | run id: 235971\n" + "status='healthy' service='zeroid' timestamp=datetime.datetime(2026, 9, 4, 20, 32, 50, 724424, tzinfo=TzInfo(0)) uptime_ms=136600 | run id: 994c76\n" ] } ], @@ -70,7 +70,7 @@ }, { "cell_type": "markdown", - "id": "00944b05", + "id": "1cade7b5", "metadata": {}, "source": [ "## 1 · Agent Registration Record — Layer 1 · The Passport (ODIS §6.1)\n", @@ -86,13 +86,13 @@ { "cell_type": "code", "execution_count": 2, - "id": "9564dc00", + "id": "84333ab0", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:30:53.752778Z", - "iopub.status.busy": "2026-09-04T20:30:53.752671Z", - "iopub.status.idle": "2026-09-04T20:30:53.761587Z", - "shell.execute_reply": "2026-09-04T20:30:53.761094Z" + "iopub.execute_input": "2026-09-04T20:32:50.729471Z", + "iopub.status.busy": "2026-09-04T20:32:50.729371Z", + "iopub.status.idle": "2026-09-04T20:32:50.738048Z", + "shell.execute_reply": "2026-09-04T20:32:50.737686Z" } }, "outputs": [ @@ -100,7 +100,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-235971 | trust: unverified | status: active\n" + "spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-994c76 | trust: unverified | status: active\n" ] } ], @@ -114,7 +114,7 @@ }, { "cell_type": "markdown", - "id": "9f6328dd", + "id": "97098c9c", "metadata": {}, "source": [ "## 2 · Agent Runtime Credential — the Passport's output (ODIS §6.2, L1-05)\n", @@ -126,13 +126,13 @@ { "cell_type": "code", "execution_count": 3, - "id": "7f3fd466", + "id": "a462a567", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:30:53.762746Z", - "iopub.status.busy": "2026-09-04T20:30:53.762667Z", - "iopub.status.idle": "2026-09-04T20:30:53.771596Z", - "shell.execute_reply": "2026-09-04T20:30:53.771116Z" + "iopub.execute_input": "2026-09-04T20:32:50.739342Z", + "iopub.status.busy": "2026-09-04T20:32:50.739240Z", + "iopub.status.idle": "2026-09-04T20:32:50.749232Z", + "shell.execute_reply": "2026-09-04T20:32:50.748834Z" } }, "outputs": [ @@ -151,7 +151,7 @@ }, { "cell_type": "markdown", - "id": "0e3ed41b", + "id": "58ff8f21", "metadata": {}, "source": [ "## 3 · The ODIS-aware target — the Bridge's native mode (ODIS §2.5 / L2-15)\n", @@ -176,13 +176,13 @@ { "cell_type": "code", "execution_count": 4, - "id": "f4b34892", + "id": "fdca0a71", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:30:53.772739Z", - "iopub.status.busy": "2026-09-04T20:30:53.772646Z", - "iopub.status.idle": "2026-09-04T20:30:53.779843Z", - "shell.execute_reply": "2026-09-04T20:30:53.779393Z" + "iopub.execute_input": "2026-09-04T20:32:50.750423Z", + "iopub.status.busy": "2026-09-04T20:32:50.750337Z", + "iopub.status.idle": "2026-09-04T20:32:50.756378Z", + "shell.execute_reply": "2026-09-04T20:32:50.756025Z" } }, "outputs": [ @@ -190,7 +190,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "verified: spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-235971 | delegated: False\n", + "verified: spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-994c76 | delegated: False\n", "refused (ZeroIDError): Missing required scope: 'admin:all'\n", "refused (ZeroIDError): Insufficient trust level: required 'first_party', got 'unverified'\n" ] @@ -211,7 +211,7 @@ }, { "cell_type": "markdown", - "id": "2dd70fbd", + "id": "ab654849", "metadata": {}, "source": [ "Audience is the first duty on ODIS §2.5's native-mode list, and it is one argument\n", @@ -223,13 +223,13 @@ { "cell_type": "code", "execution_count": 5, - "id": "1cf01465", + "id": "36ab4703", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:30:53.780991Z", - "iopub.status.busy": "2026-09-04T20:30:53.780898Z", - "iopub.status.idle": "2026-09-04T20:30:53.783581Z", - "shell.execute_reply": "2026-09-04T20:30:53.783191Z" + "iopub.execute_input": "2026-09-04T20:32:50.757580Z", + "iopub.status.busy": "2026-09-04T20:32:50.757506Z", + "iopub.status.idle": "2026-09-04T20:32:50.760288Z", + "shell.execute_reply": "2026-09-04T20:32:50.759887Z" } }, "outputs": [ @@ -254,7 +254,7 @@ }, { "cell_type": "markdown", - "id": "480da406", + "id": "5bb77b00", "metadata": {}, "source": [ "Trust is **minted into the credential at issuance** — `trust_level` is a claim. So\n", @@ -270,13 +270,13 @@ { "cell_type": "code", "execution_count": 6, - "id": "a01f99ae", + "id": "ff7f46c9", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:30:53.784886Z", - "iopub.status.busy": "2026-09-04T20:30:53.784807Z", - "iopub.status.idle": "2026-09-04T20:30:53.796512Z", - "shell.execute_reply": "2026-09-04T20:30:53.795962Z" + "iopub.execute_input": "2026-09-04T20:32:50.761395Z", + "iopub.status.busy": "2026-09-04T20:32:50.761320Z", + "iopub.status.idle": "2026-09-04T20:32:50.771455Z", + "shell.execute_reply": "2026-09-04T20:32:50.771031Z" } }, "outputs": [ @@ -298,7 +298,7 @@ }, { "cell_type": "markdown", - "id": "8de6c9ee", + "id": "656015e0", "metadata": {}, "source": [ "## 4 · Delegation Record — Layer 2 · The Bridge (ODIS §6.3, Pillar 4, L2-05/06)\n", @@ -315,13 +315,13 @@ { "cell_type": "code", "execution_count": 7, - "id": "77ebcec6", + "id": "55f41b36", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:30:53.797607Z", - "iopub.status.busy": "2026-09-04T20:30:53.797526Z", - "iopub.status.idle": "2026-09-04T20:30:53.816167Z", - "shell.execute_reply": "2026-09-04T20:30:53.815438Z" + "iopub.execute_input": "2026-09-04T20:32:50.772434Z", + "iopub.status.busy": "2026-09-04T20:32:50.772363Z", + "iopub.status.idle": "2026-09-04T20:32:50.790309Z", + "shell.execute_reply": "2026-09-04T20:32:50.789847Z" } }, "outputs": [ @@ -330,7 +330,7 @@ "output_type": "stream", "text": [ "granted scope (requested read+write): 'data:read'\n", - "sub: researcher-235971 | is_delegated: True | act: {'sub': 'spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-235971'} | depth: 1\n", + "sub: researcher-994c76 | is_delegated: True | act: {'sub': 'spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-994c76'} | depth: 1\n", "empty intersection refused (APIError): [400] invalid_scope: requested scopes are not available for delegation\n" ] } @@ -370,7 +370,7 @@ }, { "cell_type": "markdown", - "id": "3b20e461", + "id": "9f0ef43b", "metadata": {}, "source": [ "## 5 · Compromise signal, cascade — and what native mode must add — L1-12, L3-04/05\n", @@ -387,13 +387,13 @@ { "cell_type": "code", "execution_count": 8, - "id": "a461850c", + "id": "0c823ede", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:30:53.817663Z", - "iopub.status.busy": "2026-09-04T20:30:53.817540Z", - "iopub.status.idle": "2026-09-04T20:30:53.825474Z", - "shell.execute_reply": "2026-09-04T20:30:53.824977Z" + "iopub.execute_input": "2026-09-04T20:32:50.791474Z", + "iopub.status.busy": "2026-09-04T20:32:50.791384Z", + "iopub.status.idle": "2026-09-04T20:32:50.799284Z", + "shell.execute_reply": "2026-09-04T20:32:50.798787Z" } }, "outputs": [ @@ -418,7 +418,7 @@ }, { "cell_type": "markdown", - "id": "353c5040", + "id": "72c118bd", "metadata": {}, "source": [ "## What the SDK view added\n", From 56b2a52e47cb8f2395e341ce3d2d9f369c903806 Mon Sep 17 00:00:00 2001 From: Sharath Rajasekar Date: Fri, 4 Sep 2026 13:38:05 -0700 Subject: [PATCH 12/14] docs: add a commented-out prerequisite install cell to both ODIS notebooks (quickstart convention) Co-Authored-By: Claude Fable 5 --- examples/odis/odis-walkthrough-sdk.ipynb | 138 +++++++------ examples/odis/odis-walkthrough.ipynb | 241 ++++++++++++----------- 2 files changed, 204 insertions(+), 175 deletions(-) diff --git a/examples/odis/odis-walkthrough-sdk.ipynb b/examples/odis/odis-walkthrough-sdk.ipynb index 3fb2e012..c610c07d 100644 --- a/examples/odis/odis-walkthrough-sdk.ipynb +++ b/examples/odis/odis-walkthrough-sdk.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "9b718945", + "id": "b30e7187", "metadata": {}, "source": [ "# ZeroID as ODIS Layers 1–2 — the SDK companion\n", @@ -38,13 +38,31 @@ { "cell_type": "code", "execution_count": 1, - "id": "76c87892", + "id": "6d1cd3ab", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:32:50.603225Z", - "iopub.status.busy": "2026-09-04T20:32:50.602994Z", - "iopub.status.idle": "2026-09-04T20:32:50.728239Z", - "shell.execute_reply": "2026-09-04T20:32:50.727710Z" + "iopub.execute_input": "2026-09-04T20:37:54.761522Z", + "iopub.status.busy": "2026-09-04T20:37:54.761419Z", + "iopub.status.idle": "2026-09-04T20:37:54.765407Z", + "shell.execute_reply": "2026-09-04T20:37:54.764802Z" + } + }, + "outputs": [], + "source": [ + "# Uncomment to install the prerequisites into the active kernel (latest SDK release):\n", + "#!pip install -q highflame pyjwt cryptography" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "8f80f354", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-04T20:37:54.766647Z", + "iopub.status.busy": "2026-09-04T20:37:54.766554Z", + "iopub.status.idle": "2026-09-04T20:37:54.914340Z", + "shell.execute_reply": "2026-09-04T20:37:54.913855Z" } }, "outputs": [ @@ -52,7 +70,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "status='healthy' service='zeroid' timestamp=datetime.datetime(2026, 9, 4, 20, 32, 50, 724424, tzinfo=TzInfo(0)) uptime_ms=136600 | run id: 994c76\n" + "status='healthy' service='zeroid' timestamp=datetime.datetime(2026, 9, 4, 20, 37, 54, 909754, tzinfo=TzInfo(0)) uptime_ms=440782 | run id: 104d2c\n" ] } ], @@ -70,7 +88,7 @@ }, { "cell_type": "markdown", - "id": "1cade7b5", + "id": "d09435fc", "metadata": {}, "source": [ "## 1 · Agent Registration Record — Layer 1 · The Passport (ODIS §6.1)\n", @@ -85,14 +103,14 @@ }, { "cell_type": "code", - "execution_count": 2, - "id": "84333ab0", + "execution_count": 3, + "id": "030d02f4", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:32:50.729471Z", - "iopub.status.busy": "2026-09-04T20:32:50.729371Z", - "iopub.status.idle": "2026-09-04T20:32:50.738048Z", - "shell.execute_reply": "2026-09-04T20:32:50.737686Z" + "iopub.execute_input": "2026-09-04T20:37:54.915516Z", + "iopub.status.busy": "2026-09-04T20:37:54.915411Z", + "iopub.status.idle": "2026-09-04T20:37:54.925308Z", + "shell.execute_reply": "2026-09-04T20:37:54.924819Z" } }, "outputs": [ @@ -100,7 +118,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-994c76 | trust: unverified | status: active\n" + "spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-104d2c | trust: unverified | status: active\n" ] } ], @@ -114,7 +132,7 @@ }, { "cell_type": "markdown", - "id": "97098c9c", + "id": "03e5d10b", "metadata": {}, "source": [ "## 2 · Agent Runtime Credential — the Passport's output (ODIS §6.2, L1-05)\n", @@ -125,14 +143,14 @@ }, { "cell_type": "code", - "execution_count": 3, - "id": "a462a567", + "execution_count": 4, + "id": "7ade419d", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:32:50.739342Z", - "iopub.status.busy": "2026-09-04T20:32:50.739240Z", - "iopub.status.idle": "2026-09-04T20:32:50.749232Z", - "shell.execute_reply": "2026-09-04T20:32:50.748834Z" + "iopub.execute_input": "2026-09-04T20:37:54.926277Z", + "iopub.status.busy": "2026-09-04T20:37:54.926210Z", + "iopub.status.idle": "2026-09-04T20:37:54.935233Z", + "shell.execute_reply": "2026-09-04T20:37:54.934742Z" } }, "outputs": [ @@ -151,7 +169,7 @@ }, { "cell_type": "markdown", - "id": "58ff8f21", + "id": "9ff61199", "metadata": {}, "source": [ "## 3 · The ODIS-aware target — the Bridge's native mode (ODIS §2.5 / L2-15)\n", @@ -175,14 +193,14 @@ }, { "cell_type": "code", - "execution_count": 4, - "id": "fdca0a71", + "execution_count": 5, + "id": "c7b52e0a", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:32:50.750423Z", - "iopub.status.busy": "2026-09-04T20:32:50.750337Z", - "iopub.status.idle": "2026-09-04T20:32:50.756378Z", - "shell.execute_reply": "2026-09-04T20:32:50.756025Z" + "iopub.execute_input": "2026-09-04T20:37:54.936328Z", + "iopub.status.busy": "2026-09-04T20:37:54.936262Z", + "iopub.status.idle": "2026-09-04T20:37:54.943846Z", + "shell.execute_reply": "2026-09-04T20:37:54.943344Z" } }, "outputs": [ @@ -190,7 +208,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "verified: spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-994c76 | delegated: False\n", + "verified: spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-104d2c | delegated: False\n", "refused (ZeroIDError): Missing required scope: 'admin:all'\n", "refused (ZeroIDError): Insufficient trust level: required 'first_party', got 'unverified'\n" ] @@ -211,7 +229,7 @@ }, { "cell_type": "markdown", - "id": "ab654849", + "id": "a3eac52a", "metadata": {}, "source": [ "Audience is the first duty on ODIS §2.5's native-mode list, and it is one argument\n", @@ -222,14 +240,14 @@ }, { "cell_type": "code", - "execution_count": 5, - "id": "36ab4703", + "execution_count": 6, + "id": "0e4520c4", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:32:50.757580Z", - "iopub.status.busy": "2026-09-04T20:32:50.757506Z", - "iopub.status.idle": "2026-09-04T20:32:50.760288Z", - "shell.execute_reply": "2026-09-04T20:32:50.759887Z" + "iopub.execute_input": "2026-09-04T20:37:54.944941Z", + "iopub.status.busy": "2026-09-04T20:37:54.944859Z", + "iopub.status.idle": "2026-09-04T20:37:54.947558Z", + "shell.execute_reply": "2026-09-04T20:37:54.947166Z" } }, "outputs": [ @@ -254,7 +272,7 @@ }, { "cell_type": "markdown", - "id": "5bb77b00", + "id": "91a70f1e", "metadata": {}, "source": [ "Trust is **minted into the credential at issuance** — `trust_level` is a claim. So\n", @@ -269,14 +287,14 @@ }, { "cell_type": "code", - "execution_count": 6, - "id": "ff7f46c9", + "execution_count": 7, + "id": "929b347c", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:32:50.761395Z", - "iopub.status.busy": "2026-09-04T20:32:50.761320Z", - "iopub.status.idle": "2026-09-04T20:32:50.771455Z", - "shell.execute_reply": "2026-09-04T20:32:50.771031Z" + "iopub.execute_input": "2026-09-04T20:37:54.948662Z", + "iopub.status.busy": "2026-09-04T20:37:54.948597Z", + "iopub.status.idle": "2026-09-04T20:37:54.958917Z", + "shell.execute_reply": "2026-09-04T20:37:54.958445Z" } }, "outputs": [ @@ -298,7 +316,7 @@ }, { "cell_type": "markdown", - "id": "656015e0", + "id": "f3742f4d", "metadata": {}, "source": [ "## 4 · Delegation Record — Layer 2 · The Bridge (ODIS §6.3, Pillar 4, L2-05/06)\n", @@ -314,14 +332,14 @@ }, { "cell_type": "code", - "execution_count": 7, - "id": "55f41b36", + "execution_count": 8, + "id": "5784b85f", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:32:50.772434Z", - "iopub.status.busy": "2026-09-04T20:32:50.772363Z", - "iopub.status.idle": "2026-09-04T20:32:50.790309Z", - "shell.execute_reply": "2026-09-04T20:32:50.789847Z" + "iopub.execute_input": "2026-09-04T20:37:54.960066Z", + "iopub.status.busy": "2026-09-04T20:37:54.959980Z", + "iopub.status.idle": "2026-09-04T20:37:54.977696Z", + "shell.execute_reply": "2026-09-04T20:37:54.977218Z" } }, "outputs": [ @@ -330,7 +348,7 @@ "output_type": "stream", "text": [ "granted scope (requested read+write): 'data:read'\n", - "sub: researcher-994c76 | is_delegated: True | act: {'sub': 'spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-994c76'} | depth: 1\n", + "sub: researcher-104d2c | is_delegated: True | act: {'sub': 'spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-104d2c'} | depth: 1\n", "empty intersection refused (APIError): [400] invalid_scope: requested scopes are not available for delegation\n" ] } @@ -370,7 +388,7 @@ }, { "cell_type": "markdown", - "id": "9f0ef43b", + "id": "d42c95de", "metadata": {}, "source": [ "## 5 · Compromise signal, cascade — and what native mode must add — L1-12, L3-04/05\n", @@ -386,14 +404,14 @@ }, { "cell_type": "code", - "execution_count": 8, - "id": "0c823ede", + "execution_count": 9, + "id": "4d3fdfc7", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:32:50.791474Z", - "iopub.status.busy": "2026-09-04T20:32:50.791384Z", - "iopub.status.idle": "2026-09-04T20:32:50.799284Z", - "shell.execute_reply": "2026-09-04T20:32:50.798787Z" + "iopub.execute_input": "2026-09-04T20:37:54.978850Z", + "iopub.status.busy": "2026-09-04T20:37:54.978777Z", + "iopub.status.idle": "2026-09-04T20:37:54.985749Z", + "shell.execute_reply": "2026-09-04T20:37:54.985263Z" } }, "outputs": [ @@ -418,7 +436,7 @@ }, { "cell_type": "markdown", - "id": "72c118bd", + "id": "55f09df2", "metadata": {}, "source": [ "## What the SDK view added\n", diff --git a/examples/odis/odis-walkthrough.ipynb b/examples/odis/odis-walkthrough.ipynb index 7f07d498..59423e75 100644 --- a/examples/odis/odis-walkthrough.ipynb +++ b/examples/odis/odis-walkthrough.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "f0af314d", + "id": "7bc081d6", "metadata": {}, "source": [ "# ZeroID as ODIS Layers 1–2 — an executable walkthrough\n", @@ -48,13 +48,31 @@ { "cell_type": "code", "execution_count": 1, - "id": "73e74ab7", + "id": "0ebadc76", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:30:52.268795Z", - "iopub.status.busy": "2026-09-04T20:30:52.268507Z", - "iopub.status.idle": "2026-09-04T20:30:52.339785Z", - "shell.execute_reply": "2026-09-04T20:30:52.339289Z" + "iopub.execute_input": "2026-09-04T20:37:53.456883Z", + "iopub.status.busy": "2026-09-04T20:37:53.456719Z", + "iopub.status.idle": "2026-09-04T20:37:53.461786Z", + "shell.execute_reply": "2026-09-04T20:37:53.461114Z" + } + }, + "outputs": [], + "source": [ + "# Uncomment to install the prerequisites into the active kernel:\n", + "#!pip install -q requests pyjwt cryptography" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "082a252f", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-04T20:37:53.463822Z", + "iopub.status.busy": "2026-09-04T20:37:53.463638Z", + "iopub.status.idle": "2026-09-04T20:37:53.554828Z", + "shell.execute_reply": "2026-09-04T20:37:53.554339Z" } }, "outputs": [ @@ -62,7 +80,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "zeroid: healthy | run id: e77a4d\n" + "zeroid: healthy | run id: e697bf\n" ] } ], @@ -100,7 +118,7 @@ }, { "cell_type": "markdown", - "id": "4e50a083", + "id": "943ce3e1", "metadata": {}, "source": [ "## 1 · Agent Registration Record — Layer 1 · The Passport (ODIS §6.1)\n", @@ -114,14 +132,14 @@ }, { "cell_type": "code", - "execution_count": 2, - "id": "d5bd8c0d", + "execution_count": 3, + "id": "1a2ebafc", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:30:52.341125Z", - "iopub.status.busy": "2026-09-04T20:30:52.341015Z", - "iopub.status.idle": "2026-09-04T20:30:52.369522Z", - "shell.execute_reply": "2026-09-04T20:30:52.369088Z" + "iopub.execute_input": "2026-09-04T20:37:53.555909Z", + "iopub.status.busy": "2026-09-04T20:37:53.555812Z", + "iopub.status.idle": "2026-09-04T20:37:53.573421Z", + "shell.execute_reply": "2026-09-04T20:37:53.572989Z" } }, "outputs": [ @@ -130,7 +148,7 @@ "output_type": "stream", "text": [ "{\n", - " \"wimse_uri\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-e77a4d\",\n", + " \"wimse_uri\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-e697bf\",\n", " \"identity_type\": \"agent\",\n", " \"trust_level\": \"unverified\",\n", " \"status\": \"active\",\n", @@ -153,7 +171,7 @@ }, { "cell_type": "markdown", - "id": "2421b53d", + "id": "5abeb5ae", "metadata": {}, "source": [ "## 2 · Registration-record governance fields — Layer 1 · The Passport (ODIS §6.1)\n", @@ -168,14 +186,14 @@ }, { "cell_type": "code", - "execution_count": 3, - "id": "2b59471c", + "execution_count": 4, + "id": "8176fdd3", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:30:52.370890Z", - "iopub.status.busy": "2026-09-04T20:30:52.370811Z", - "iopub.status.idle": "2026-09-04T20:30:52.383949Z", - "shell.execute_reply": "2026-09-04T20:30:52.383512Z" + "iopub.execute_input": "2026-09-04T20:37:53.574533Z", + "iopub.status.busy": "2026-09-04T20:37:53.574463Z", + "iopub.status.idle": "2026-09-04T20:37:53.584499Z", + "shell.execute_reply": "2026-09-04T20:37:53.584128Z" } }, "outputs": [ @@ -184,7 +202,7 @@ "output_type": "stream", "text": [ "{\n", - " \"name\": \"odis-demo-e77a4d\",\n", + " \"name\": \"odis-demo-e697bf\",\n", " \"max_ttl_seconds\": 3600,\n", " \"required_trust_level\": \"first_party\",\n", " \"max_delegation_depth\": 2\n", @@ -208,7 +226,7 @@ }, { "cell_type": "markdown", - "id": "7c01ce5d", + "id": "aa1fd1ed", "metadata": {}, "source": [ "## 3 · No registration resolution, no authority — The Bridge refuses (ODIS-L2-14 + L1-11)\n", @@ -220,14 +238,14 @@ }, { "cell_type": "code", - "execution_count": 4, - "id": "30384af4", + "execution_count": 5, + "id": "e1dbd1d4", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:30:52.385300Z", - "iopub.status.busy": "2026-09-04T20:30:52.385207Z", - "iopub.status.idle": "2026-09-04T20:30:52.393810Z", - "shell.execute_reply": "2026-09-04T20:30:52.393357Z" + "iopub.execute_input": "2026-09-04T20:37:53.585578Z", + "iopub.status.busy": "2026-09-04T20:37:53.585498Z", + "iopub.status.idle": "2026-09-04T20:37:53.591958Z", + "shell.execute_reply": "2026-09-04T20:37:53.591382Z" } }, "outputs": [ @@ -251,7 +269,7 @@ }, { "cell_type": "markdown", - "id": "21062c6f", + "id": "ddce0206", "metadata": {}, "source": [ "## 4 · Attestation raises trust — Layer 1 · The Passport (ODIS-L1-03 / L1-11)\n", @@ -267,14 +285,14 @@ }, { "cell_type": "code", - "execution_count": 5, - "id": "cfc45869", + "execution_count": 6, + "id": "55edb809", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:30:52.395100Z", - "iopub.status.busy": "2026-09-04T20:30:52.395029Z", - "iopub.status.idle": "2026-09-04T20:30:52.412485Z", - "shell.execute_reply": "2026-09-04T20:30:52.412037Z" + "iopub.execute_input": "2026-09-04T20:37:53.593107Z", + "iopub.status.busy": "2026-09-04T20:37:53.593023Z", + "iopub.status.idle": "2026-09-04T20:37:53.606806Z", + "shell.execute_reply": "2026-09-04T20:37:53.606345Z" } }, "outputs": [ @@ -299,7 +317,7 @@ }, { "cell_type": "markdown", - "id": "194e33d3", + "id": "0d43d0c9", "metadata": {}, "source": [ "## 5 · Agent Runtime Credential — the Passport's output (ODIS §6.2, L1-05 / L1-09)\n", @@ -319,14 +337,14 @@ }, { "cell_type": "code", - "execution_count": 6, - "id": "3db7ab5c", + "execution_count": 7, + "id": "d81a7b1d", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:30:52.413690Z", - "iopub.status.busy": "2026-09-04T20:30:52.413608Z", - "iopub.status.idle": "2026-09-04T20:30:52.429968Z", - "shell.execute_reply": "2026-09-04T20:30:52.429492Z" + "iopub.execute_input": "2026-09-04T20:37:53.607907Z", + "iopub.status.busy": "2026-09-04T20:37:53.607833Z", + "iopub.status.idle": "2026-09-04T20:37:53.621197Z", + "shell.execute_reply": "2026-09-04T20:37:53.620733Z" } }, "outputs": [ @@ -336,12 +354,12 @@ "text": [ "token_type: DPoP | expires_in: 3600 s\n", "{\n", - " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-e77a4d\",\n", + " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-e697bf\",\n", " \"trust_level\": \"first_party\",\n", - " \"mission_id\": \"b8f71064-cebc-479b-8c27-ab61ba2f3e62\",\n", - " \"jti\": \"b8f71064-cebc-479b-8c27-ab61ba2f3e62\"\n", + " \"mission_id\": \"e0e596a7-7877-4cd1-98d4-d0c91db46264\",\n", + " \"jti\": \"e0e596a7-7877-4cd1-98d4-d0c91db46264\"\n", "}\n", - "cnf: {'jkt': 'g5qlBflyMag7ECGtBO66ZFOs33AKSAmIUxiVzs9S-Cw'}\n" + "cnf: {'jkt': 'KJDVZfHut3haAnvzWBerWVieGewjnOm7Jt2yrgnlYxw'}\n" ] } ], @@ -361,7 +379,7 @@ }, { "cell_type": "markdown", - "id": "f1ca8262", + "id": "a2a5a837", "metadata": {}, "source": [ "A replayed proof must be rejected — the `jti` ledger is atomic (ODIS's replay concern\n", @@ -370,14 +388,14 @@ }, { "cell_type": "code", - "execution_count": 7, - "id": "52b1234c", + "execution_count": 8, + "id": "0f4c024e", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:30:52.431046Z", - "iopub.status.busy": "2026-09-04T20:30:52.430954Z", - "iopub.status.idle": "2026-09-04T20:30:52.435977Z", - "shell.execute_reply": "2026-09-04T20:30:52.435567Z" + "iopub.execute_input": "2026-09-04T20:37:53.622163Z", + "iopub.status.busy": "2026-09-04T20:37:53.622084Z", + "iopub.status.idle": "2026-09-04T20:37:53.626669Z", + "shell.execute_reply": "2026-09-04T20:37:53.626315Z" } }, "outputs": [ @@ -402,7 +420,7 @@ }, { "cell_type": "markdown", - "id": "736cbbc9", + "id": "2f421145", "metadata": {}, "source": [ "## 6 · Delegation Record — Layer 2 · The Bridge (ODIS §6.3, Pillar 4, L2-01/05/06)\n", @@ -419,14 +437,14 @@ }, { "cell_type": "code", - "execution_count": 8, - "id": "1557c62f", + "execution_count": 9, + "id": "a906c986", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:30:52.437020Z", - "iopub.status.busy": "2026-09-04T20:30:52.436942Z", - "iopub.status.idle": "2026-09-04T20:30:52.460554Z", - "shell.execute_reply": "2026-09-04T20:30:52.460061Z" + "iopub.execute_input": "2026-09-04T20:37:53.627778Z", + "iopub.status.busy": "2026-09-04T20:37:53.627709Z", + "iopub.status.idle": "2026-09-04T20:37:53.647668Z", + "shell.execute_reply": "2026-09-04T20:37:53.647310Z" } }, "outputs": [ @@ -435,13 +453,13 @@ "output_type": "stream", "text": [ "{\n", - " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/researcher-e77a4d\",\n", + " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/researcher-e697bf\",\n", " \"act\": {\n", - " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-e77a4d\"\n", + " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-e697bf\"\n", " },\n", " \"delegation_depth\": 1,\n", - " \"mission_id\": \"b8f71064-cebc-479b-8c27-ab61ba2f3e62\",\n", - " \"jti\": \"72c1ce23-188d-4e14-a93b-eaa0235750ce\"\n", + " \"mission_id\": \"e0e596a7-7877-4cd1-98d4-d0c91db46264\",\n", + " \"jti\": \"0151d06b-ce71-4711-8b07-671f6b9a535e\"\n", "}\n" ] } @@ -476,7 +494,7 @@ }, { "cell_type": "markdown", - "id": "1c487403", + "id": "c05483d7", "metadata": {}, "source": [ "Attenuation is **monotonic**: the researcher's policy ceiling is `data:read`, so a\n", @@ -488,14 +506,14 @@ }, { "cell_type": "code", - "execution_count": 9, - "id": "c4192f3f", + "execution_count": 10, + "id": "7b59ec04", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:30:52.461621Z", - "iopub.status.busy": "2026-09-04T20:30:52.461539Z", - "iopub.status.idle": "2026-09-04T20:30:52.471041Z", - "shell.execute_reply": "2026-09-04T20:30:52.470645Z" + "iopub.execute_input": "2026-09-04T20:37:53.648621Z", + "iopub.status.busy": "2026-09-04T20:37:53.648552Z", + "iopub.status.idle": "2026-09-04T20:37:53.657633Z", + "shell.execute_reply": "2026-09-04T20:37:53.657152Z" } }, "outputs": [ @@ -529,7 +547,7 @@ }, { "cell_type": "markdown", - "id": "8a680649", + "id": "81a8278b", "metadata": {}, "source": [ "## 7 · Bounded asynchronous human authorization — Layer 2 · The Bridge (ODIS-L2-02, Pillar 1)\n", @@ -551,14 +569,14 @@ }, { "cell_type": "code", - "execution_count": 10, - "id": "9dfc5520", + "execution_count": 11, + "id": "ad64095e", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:30:52.472153Z", - "iopub.status.busy": "2026-09-04T20:30:52.472076Z", - "iopub.status.idle": "2026-09-04T20:30:52.801324Z", - "shell.execute_reply": "2026-09-04T20:30:52.798208Z" + "iopub.execute_input": "2026-09-04T20:37:53.658623Z", + "iopub.status.busy": "2026-09-04T20:37:53.658551Z", + "iopub.status.idle": "2026-09-04T20:37:53.973891Z", + "shell.execute_reply": "2026-09-04T20:37:53.972964Z" } }, "outputs": [ @@ -566,16 +584,9 @@ "name": "stdout", "output_type": "stream", "text": [ - "bc-authorize:" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 200\n", + "bc-authorize: 200\n", "{\n", - " \"auth_req_id\": \"CJhAxkGirS5ZMc0KC2xZanLnYdhg7YAhXr71dCQ1U4U\",\n", + " \"auth_req_id\": \"3zwLJHOC6kCxC6Rs53_ZGAK5I4dwgH8mOA4VVf8sg_4\",\n", " \"expires_in\": 300,\n", " \"interval\": 5\n", "}\n" @@ -629,7 +640,7 @@ }, { "cell_type": "markdown", - "id": "f965ca71", + "id": "998ca7a2", "metadata": {}, "source": [ "## 8 · Confirmed compromise → cascade revocation — ODIS-L1-12, L3-04 / L3-05\n", @@ -642,14 +653,14 @@ }, { "cell_type": "code", - "execution_count": 11, - "id": "e5bed532", + "execution_count": 12, + "id": "9c5fc5de", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:30:52.805034Z", - "iopub.status.busy": "2026-09-04T20:30:52.804744Z", - "iopub.status.idle": "2026-09-04T20:30:52.825985Z", - "shell.execute_reply": "2026-09-04T20:30:52.824720Z" + "iopub.execute_input": "2026-09-04T20:37:53.976582Z", + "iopub.status.busy": "2026-09-04T20:37:53.976286Z", + "iopub.status.idle": "2026-09-04T20:37:53.994878Z", + "shell.execute_reply": "2026-09-04T20:37:53.993986Z" } }, "outputs": [ @@ -657,7 +668,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "delegated token after cascade: {'active': False} (15 ms signal→dead)\n" + "delegated token after cascade: {'active': False} (13 ms signal→dead)\n" ] } ], @@ -673,7 +684,7 @@ }, { "cell_type": "markdown", - "id": "4feae2db", + "id": "c3b9efdd", "metadata": {}, "source": [ "## 9 · Audit lineage survives the kill — ODIS-CC-01 / CC-02\n", @@ -685,14 +696,14 @@ }, { "cell_type": "code", - "execution_count": 12, - "id": "41ff4b22", + "execution_count": 13, + "id": "090e70c5", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:30:52.828624Z", - "iopub.status.busy": "2026-09-04T20:30:52.828407Z", - "iopub.status.idle": "2026-09-04T20:30:52.840067Z", - "shell.execute_reply": "2026-09-04T20:30:52.839274Z" + "iopub.execute_input": "2026-09-04T20:37:53.997732Z", + "iopub.status.busy": "2026-09-04T20:37:53.997475Z", + "iopub.status.idle": "2026-09-04T20:37:54.009042Z", + "shell.execute_reply": "2026-09-04T20:37:54.007640Z" } }, "outputs": [ @@ -700,7 +711,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "chain: orch-e77a4d → researcher-e77a4d\n", + "chain: orch-e697bf → researcher-e697bf\n", "[\n", " {\n", " \"grant_type\": \"api_key\",\n", @@ -715,7 +726,7 @@ " ],\n", " \"attenuated\": [],\n", " \"is_revoked\": true,\n", - " \"revoke_reason\": \"auto-revoked by CAE signal 04b02137-8b78-40d1-b0a5-aeb9d9d2adda (severity: critical)\"\n", + " \"revoke_reason\": \"auto-revoked by CAE signal 0b11ebf2-d1d8-4e2a-9a1b-9348b57a4d4c (severity: critical)\"\n", " },\n", " {\n", " \"grant_type\": \"token_exchange\",\n", @@ -731,7 +742,7 @@ " \"data:write\"\n", " ],\n", " \"is_revoked\": true,\n", - " \"revoke_reason\": \"auto-revoked by CAE signal 04b02137-8b78-40d1-b0a5-aeb9d9d2adda (severity: critical)\"\n", + " \"revoke_reason\": \"auto-revoked by CAE signal 0b11ebf2-d1d8-4e2a-9a1b-9348b57a4d4c (severity: critical)\"\n", " }\n", "]\n" ] @@ -746,7 +757,7 @@ }, { "cell_type": "markdown", - "id": "c7a50761", + "id": "45a7e934", "metadata": {}, "source": [ "## 10 · The sponsor leaves — offboarding kill switch (ODIS-L1-06 / L1-10 / L3-05)\n", @@ -766,14 +777,14 @@ }, { "cell_type": "code", - "execution_count": 13, - "id": "d8041dac", + "execution_count": 14, + "id": "5bf2ac58", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:30:52.842783Z", - "iopub.status.busy": "2026-09-04T20:30:52.842556Z", - "iopub.status.idle": "2026-09-04T20:30:52.862157Z", - "shell.execute_reply": "2026-09-04T20:30:52.861445Z" + "iopub.execute_input": "2026-09-04T20:37:54.011451Z", + "iopub.status.busy": "2026-09-04T20:37:54.011346Z", + "iopub.status.idle": "2026-09-04T20:37:54.031143Z", + "shell.execute_reply": "2026-09-04T20:37:54.030375Z" } }, "outputs": [ @@ -811,7 +822,7 @@ }, { "cell_type": "markdown", - "id": "f8b13be7", + "id": "3a7b11f0", "metadata": {}, "source": [ "## What you just saw, in ODIS terms\n", From 1d4e8db0150e56678b4a753d6e54ebe29fee59e1 Mon Sep 17 00:00:00 2001 From: Sharath Rajasekar Date: Fri, 4 Sep 2026 13:39:22 -0700 Subject: [PATCH 13/14] docs: notebook install cells use the %pip magic behind a plain comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Uncommenting the previous '#!pip install ...' form left a bare 'pip install' line — a SyntaxError. '# %pip install ...' uncomments to a valid IPython magic that installs into the kernel's own environment. Same fix applied to the quickstart's identical cell. Co-Authored-By: Claude Fable 5 --- examples/odis/odis-walkthrough-sdk.ipynb | 120 ++++++------- examples/odis/odis-walkthrough.ipynb | 204 +++++++++++------------ examples/zeroid_quickstart.ipynb | 3 +- 3 files changed, 164 insertions(+), 163 deletions(-) diff --git a/examples/odis/odis-walkthrough-sdk.ipynb b/examples/odis/odis-walkthrough-sdk.ipynb index c610c07d..f5ef2049 100644 --- a/examples/odis/odis-walkthrough-sdk.ipynb +++ b/examples/odis/odis-walkthrough-sdk.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "b30e7187", + "id": "16774192", "metadata": {}, "source": [ "# ZeroID as ODIS Layers 1–2 — the SDK companion\n", @@ -38,31 +38,31 @@ { "cell_type": "code", "execution_count": 1, - "id": "6d1cd3ab", + "id": "17f67afa", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:37:54.761522Z", - "iopub.status.busy": "2026-09-04T20:37:54.761419Z", - "iopub.status.idle": "2026-09-04T20:37:54.765407Z", - "shell.execute_reply": "2026-09-04T20:37:54.764802Z" + "iopub.execute_input": "2026-09-04T20:39:06.522823Z", + "iopub.status.busy": "2026-09-04T20:39:06.522511Z", + "iopub.status.idle": "2026-09-04T20:39:06.528407Z", + "shell.execute_reply": "2026-09-04T20:39:06.527789Z" } }, "outputs": [], "source": [ - "# Uncomment to install the prerequisites into the active kernel (latest SDK release):\n", - "#!pip install -q highflame pyjwt cryptography" + "# Uncomment the next line to install the prerequisites into this kernel (latest SDK release):\n", + "# %pip install -q highflame pyjwt cryptography" ] }, { "cell_type": "code", "execution_count": 2, - "id": "8f80f354", + "id": "6438ccd6", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:37:54.766647Z", - "iopub.status.busy": "2026-09-04T20:37:54.766554Z", - "iopub.status.idle": "2026-09-04T20:37:54.914340Z", - "shell.execute_reply": "2026-09-04T20:37:54.913855Z" + "iopub.execute_input": "2026-09-04T20:39:06.530838Z", + "iopub.status.busy": "2026-09-04T20:39:06.530662Z", + "iopub.status.idle": "2026-09-04T20:39:06.681320Z", + "shell.execute_reply": "2026-09-04T20:39:06.680844Z" } }, "outputs": [ @@ -70,7 +70,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "status='healthy' service='zeroid' timestamp=datetime.datetime(2026, 9, 4, 20, 37, 54, 909754, tzinfo=TzInfo(0)) uptime_ms=440782 | run id: 104d2c\n" + "status='healthy' service='zeroid' timestamp=datetime.datetime(2026, 9, 4, 20, 39, 6, 673991, tzinfo=TzInfo(0)) uptime_ms=512550 | run id: bfc866\n" ] } ], @@ -88,7 +88,7 @@ }, { "cell_type": "markdown", - "id": "d09435fc", + "id": "49aad371", "metadata": {}, "source": [ "## 1 · Agent Registration Record — Layer 1 · The Passport (ODIS §6.1)\n", @@ -104,13 +104,13 @@ { "cell_type": "code", "execution_count": 3, - "id": "030d02f4", + "id": "882abba3", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:37:54.915516Z", - "iopub.status.busy": "2026-09-04T20:37:54.915411Z", - "iopub.status.idle": "2026-09-04T20:37:54.925308Z", - "shell.execute_reply": "2026-09-04T20:37:54.924819Z" + "iopub.execute_input": "2026-09-04T20:39:06.682765Z", + "iopub.status.busy": "2026-09-04T20:39:06.682647Z", + "iopub.status.idle": "2026-09-04T20:39:06.691940Z", + "shell.execute_reply": "2026-09-04T20:39:06.691593Z" } }, "outputs": [ @@ -118,7 +118,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-104d2c | trust: unverified | status: active\n" + "spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-bfc866 | trust: unverified | status: active\n" ] } ], @@ -132,7 +132,7 @@ }, { "cell_type": "markdown", - "id": "03e5d10b", + "id": "14be9339", "metadata": {}, "source": [ "## 2 · Agent Runtime Credential — the Passport's output (ODIS §6.2, L1-05)\n", @@ -144,13 +144,13 @@ { "cell_type": "code", "execution_count": 4, - "id": "7ade419d", + "id": "896280e8", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:37:54.926277Z", - "iopub.status.busy": "2026-09-04T20:37:54.926210Z", - "iopub.status.idle": "2026-09-04T20:37:54.935233Z", - "shell.execute_reply": "2026-09-04T20:37:54.934742Z" + "iopub.execute_input": "2026-09-04T20:39:06.693263Z", + "iopub.status.busy": "2026-09-04T20:39:06.693168Z", + "iopub.status.idle": "2026-09-04T20:39:06.702504Z", + "shell.execute_reply": "2026-09-04T20:39:06.702077Z" } }, "outputs": [ @@ -169,7 +169,7 @@ }, { "cell_type": "markdown", - "id": "9ff61199", + "id": "7b60cfc3", "metadata": {}, "source": [ "## 3 · The ODIS-aware target — the Bridge's native mode (ODIS §2.5 / L2-15)\n", @@ -194,13 +194,13 @@ { "cell_type": "code", "execution_count": 5, - "id": "c7b52e0a", + "id": "656c8182", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:37:54.936328Z", - "iopub.status.busy": "2026-09-04T20:37:54.936262Z", - "iopub.status.idle": "2026-09-04T20:37:54.943846Z", - "shell.execute_reply": "2026-09-04T20:37:54.943344Z" + "iopub.execute_input": "2026-09-04T20:39:06.703560Z", + "iopub.status.busy": "2026-09-04T20:39:06.703485Z", + "iopub.status.idle": "2026-09-04T20:39:06.709316Z", + "shell.execute_reply": "2026-09-04T20:39:06.708886Z" } }, "outputs": [ @@ -208,7 +208,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "verified: spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-104d2c | delegated: False\n", + "verified: spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-bfc866 | delegated: False\n", "refused (ZeroIDError): Missing required scope: 'admin:all'\n", "refused (ZeroIDError): Insufficient trust level: required 'first_party', got 'unverified'\n" ] @@ -229,7 +229,7 @@ }, { "cell_type": "markdown", - "id": "a3eac52a", + "id": "23178d92", "metadata": {}, "source": [ "Audience is the first duty on ODIS §2.5's native-mode list, and it is one argument\n", @@ -241,13 +241,13 @@ { "cell_type": "code", "execution_count": 6, - "id": "0e4520c4", + "id": "34ff0abe", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:37:54.944941Z", - "iopub.status.busy": "2026-09-04T20:37:54.944859Z", - "iopub.status.idle": "2026-09-04T20:37:54.947558Z", - "shell.execute_reply": "2026-09-04T20:37:54.947166Z" + "iopub.execute_input": "2026-09-04T20:39:06.710444Z", + "iopub.status.busy": "2026-09-04T20:39:06.710362Z", + "iopub.status.idle": "2026-09-04T20:39:06.713113Z", + "shell.execute_reply": "2026-09-04T20:39:06.712677Z" } }, "outputs": [ @@ -272,7 +272,7 @@ }, { "cell_type": "markdown", - "id": "91a70f1e", + "id": "0e876a51", "metadata": {}, "source": [ "Trust is **minted into the credential at issuance** — `trust_level` is a claim. So\n", @@ -288,13 +288,13 @@ { "cell_type": "code", "execution_count": 7, - "id": "929b347c", + "id": "5dbdf0d5", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:37:54.948662Z", - "iopub.status.busy": "2026-09-04T20:37:54.948597Z", - "iopub.status.idle": "2026-09-04T20:37:54.958917Z", - "shell.execute_reply": "2026-09-04T20:37:54.958445Z" + "iopub.execute_input": "2026-09-04T20:39:06.714201Z", + "iopub.status.busy": "2026-09-04T20:39:06.714134Z", + "iopub.status.idle": "2026-09-04T20:39:06.725683Z", + "shell.execute_reply": "2026-09-04T20:39:06.725248Z" } }, "outputs": [ @@ -316,7 +316,7 @@ }, { "cell_type": "markdown", - "id": "f3742f4d", + "id": "989cb701", "metadata": {}, "source": [ "## 4 · Delegation Record — Layer 2 · The Bridge (ODIS §6.3, Pillar 4, L2-05/06)\n", @@ -333,13 +333,13 @@ { "cell_type": "code", "execution_count": 8, - "id": "5784b85f", + "id": "f542bb9d", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:37:54.960066Z", - "iopub.status.busy": "2026-09-04T20:37:54.959980Z", - "iopub.status.idle": "2026-09-04T20:37:54.977696Z", - "shell.execute_reply": "2026-09-04T20:37:54.977218Z" + "iopub.execute_input": "2026-09-04T20:39:06.726777Z", + "iopub.status.busy": "2026-09-04T20:39:06.726690Z", + "iopub.status.idle": "2026-09-04T20:39:06.742734Z", + "shell.execute_reply": "2026-09-04T20:39:06.742296Z" } }, "outputs": [ @@ -348,7 +348,7 @@ "output_type": "stream", "text": [ "granted scope (requested read+write): 'data:read'\n", - "sub: researcher-104d2c | is_delegated: True | act: {'sub': 'spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-104d2c'} | depth: 1\n", + "sub: researcher-bfc866 | is_delegated: True | act: {'sub': 'spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-bfc866'} | depth: 1\n", "empty intersection refused (APIError): [400] invalid_scope: requested scopes are not available for delegation\n" ] } @@ -388,7 +388,7 @@ }, { "cell_type": "markdown", - "id": "d42c95de", + "id": "e659a8c7", "metadata": {}, "source": [ "## 5 · Compromise signal, cascade — and what native mode must add — L1-12, L3-04/05\n", @@ -405,13 +405,13 @@ { "cell_type": "code", "execution_count": 9, - "id": "4d3fdfc7", + "id": "d29d16fc", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:37:54.978850Z", - "iopub.status.busy": "2026-09-04T20:37:54.978777Z", - "iopub.status.idle": "2026-09-04T20:37:54.985749Z", - "shell.execute_reply": "2026-09-04T20:37:54.985263Z" + "iopub.execute_input": "2026-09-04T20:39:06.743802Z", + "iopub.status.busy": "2026-09-04T20:39:06.743724Z", + "iopub.status.idle": "2026-09-04T20:39:06.749458Z", + "shell.execute_reply": "2026-09-04T20:39:06.749096Z" } }, "outputs": [ @@ -436,7 +436,7 @@ }, { "cell_type": "markdown", - "id": "55f09df2", + "id": "babc69f1", "metadata": {}, "source": [ "## What the SDK view added\n", diff --git a/examples/odis/odis-walkthrough.ipynb b/examples/odis/odis-walkthrough.ipynb index 59423e75..fec494d9 100644 --- a/examples/odis/odis-walkthrough.ipynb +++ b/examples/odis/odis-walkthrough.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "7bc081d6", + "id": "b826f0a7", "metadata": {}, "source": [ "# ZeroID as ODIS Layers 1–2 — an executable walkthrough\n", @@ -48,31 +48,31 @@ { "cell_type": "code", "execution_count": 1, - "id": "0ebadc76", + "id": "a8bebe81", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:37:53.456883Z", - "iopub.status.busy": "2026-09-04T20:37:53.456719Z", - "iopub.status.idle": "2026-09-04T20:37:53.461786Z", - "shell.execute_reply": "2026-09-04T20:37:53.461114Z" + "iopub.execute_input": "2026-09-04T20:39:05.219781Z", + "iopub.status.busy": "2026-09-04T20:39:05.219541Z", + "iopub.status.idle": "2026-09-04T20:39:05.226207Z", + "shell.execute_reply": "2026-09-04T20:39:05.225198Z" } }, "outputs": [], "source": [ - "# Uncomment to install the prerequisites into the active kernel:\n", - "#!pip install -q requests pyjwt cryptography" + "# Uncomment the next line to install the prerequisites into this kernel:\n", + "# %pip install -q requests pyjwt cryptography" ] }, { "cell_type": "code", "execution_count": 2, - "id": "082a252f", + "id": "9447d1e4", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:37:53.463822Z", - "iopub.status.busy": "2026-09-04T20:37:53.463638Z", - "iopub.status.idle": "2026-09-04T20:37:53.554828Z", - "shell.execute_reply": "2026-09-04T20:37:53.554339Z" + "iopub.execute_input": "2026-09-04T20:39:05.228665Z", + "iopub.status.busy": "2026-09-04T20:39:05.228429Z", + "iopub.status.idle": "2026-09-04T20:39:05.308155Z", + "shell.execute_reply": "2026-09-04T20:39:05.307581Z" } }, "outputs": [ @@ -80,7 +80,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "zeroid: healthy | run id: e697bf\n" + "zeroid: healthy | run id: 7f3724\n" ] } ], @@ -118,7 +118,7 @@ }, { "cell_type": "markdown", - "id": "943ce3e1", + "id": "e71dc210", "metadata": {}, "source": [ "## 1 · Agent Registration Record — Layer 1 · The Passport (ODIS §6.1)\n", @@ -133,13 +133,13 @@ { "cell_type": "code", "execution_count": 3, - "id": "1a2ebafc", + "id": "b4c42f75", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:37:53.555909Z", - "iopub.status.busy": "2026-09-04T20:37:53.555812Z", - "iopub.status.idle": "2026-09-04T20:37:53.573421Z", - "shell.execute_reply": "2026-09-04T20:37:53.572989Z" + "iopub.execute_input": "2026-09-04T20:39:05.309320Z", + "iopub.status.busy": "2026-09-04T20:39:05.309209Z", + "iopub.status.idle": "2026-09-04T20:39:05.322027Z", + "shell.execute_reply": "2026-09-04T20:39:05.321457Z" } }, "outputs": [ @@ -148,7 +148,7 @@ "output_type": "stream", "text": [ "{\n", - " \"wimse_uri\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-e697bf\",\n", + " \"wimse_uri\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-7f3724\",\n", " \"identity_type\": \"agent\",\n", " \"trust_level\": \"unverified\",\n", " \"status\": \"active\",\n", @@ -171,7 +171,7 @@ }, { "cell_type": "markdown", - "id": "5abeb5ae", + "id": "0fcc2db8", "metadata": {}, "source": [ "## 2 · Registration-record governance fields — Layer 1 · The Passport (ODIS §6.1)\n", @@ -187,13 +187,13 @@ { "cell_type": "code", "execution_count": 4, - "id": "8176fdd3", + "id": "a5733d80", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:37:53.574533Z", - "iopub.status.busy": "2026-09-04T20:37:53.574463Z", - "iopub.status.idle": "2026-09-04T20:37:53.584499Z", - "shell.execute_reply": "2026-09-04T20:37:53.584128Z" + "iopub.execute_input": "2026-09-04T20:39:05.323307Z", + "iopub.status.busy": "2026-09-04T20:39:05.323219Z", + "iopub.status.idle": "2026-09-04T20:39:05.334554Z", + "shell.execute_reply": "2026-09-04T20:39:05.334098Z" } }, "outputs": [ @@ -202,7 +202,7 @@ "output_type": "stream", "text": [ "{\n", - " \"name\": \"odis-demo-e697bf\",\n", + " \"name\": \"odis-demo-7f3724\",\n", " \"max_ttl_seconds\": 3600,\n", " \"required_trust_level\": \"first_party\",\n", " \"max_delegation_depth\": 2\n", @@ -226,7 +226,7 @@ }, { "cell_type": "markdown", - "id": "aa1fd1ed", + "id": "66a5e853", "metadata": {}, "source": [ "## 3 · No registration resolution, no authority — The Bridge refuses (ODIS-L2-14 + L1-11)\n", @@ -239,13 +239,13 @@ { "cell_type": "code", "execution_count": 5, - "id": "e1dbd1d4", + "id": "4aec1151", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:37:53.585578Z", - "iopub.status.busy": "2026-09-04T20:37:53.585498Z", - "iopub.status.idle": "2026-09-04T20:37:53.591958Z", - "shell.execute_reply": "2026-09-04T20:37:53.591382Z" + "iopub.execute_input": "2026-09-04T20:39:05.335701Z", + "iopub.status.busy": "2026-09-04T20:39:05.335608Z", + "iopub.status.idle": "2026-09-04T20:39:05.342892Z", + "shell.execute_reply": "2026-09-04T20:39:05.342432Z" } }, "outputs": [ @@ -269,7 +269,7 @@ }, { "cell_type": "markdown", - "id": "ddce0206", + "id": "cc6dbe99", "metadata": {}, "source": [ "## 4 · Attestation raises trust — Layer 1 · The Passport (ODIS-L1-03 / L1-11)\n", @@ -286,13 +286,13 @@ { "cell_type": "code", "execution_count": 6, - "id": "55edb809", + "id": "92c7c238", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:37:53.593107Z", - "iopub.status.busy": "2026-09-04T20:37:53.593023Z", - "iopub.status.idle": "2026-09-04T20:37:53.606806Z", - "shell.execute_reply": "2026-09-04T20:37:53.606345Z" + "iopub.execute_input": "2026-09-04T20:39:05.344187Z", + "iopub.status.busy": "2026-09-04T20:39:05.344085Z", + "iopub.status.idle": "2026-09-04T20:39:05.360712Z", + "shell.execute_reply": "2026-09-04T20:39:05.360212Z" } }, "outputs": [ @@ -317,7 +317,7 @@ }, { "cell_type": "markdown", - "id": "0d43d0c9", + "id": "050225c6", "metadata": {}, "source": [ "## 5 · Agent Runtime Credential — the Passport's output (ODIS §6.2, L1-05 / L1-09)\n", @@ -338,13 +338,13 @@ { "cell_type": "code", "execution_count": 7, - "id": "d81a7b1d", + "id": "32440c89", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:37:53.607907Z", - "iopub.status.busy": "2026-09-04T20:37:53.607833Z", - "iopub.status.idle": "2026-09-04T20:37:53.621197Z", - "shell.execute_reply": "2026-09-04T20:37:53.620733Z" + "iopub.execute_input": "2026-09-04T20:39:05.361708Z", + "iopub.status.busy": "2026-09-04T20:39:05.361631Z", + "iopub.status.idle": "2026-09-04T20:39:05.373473Z", + "shell.execute_reply": "2026-09-04T20:39:05.373031Z" } }, "outputs": [ @@ -354,12 +354,12 @@ "text": [ "token_type: DPoP | expires_in: 3600 s\n", "{\n", - " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-e697bf\",\n", + " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-7f3724\",\n", " \"trust_level\": \"first_party\",\n", - " \"mission_id\": \"e0e596a7-7877-4cd1-98d4-d0c91db46264\",\n", - " \"jti\": \"e0e596a7-7877-4cd1-98d4-d0c91db46264\"\n", + " \"mission_id\": \"b1284566-711c-4dc5-bdd5-67321706fa80\",\n", + " \"jti\": \"b1284566-711c-4dc5-bdd5-67321706fa80\"\n", "}\n", - "cnf: {'jkt': 'KJDVZfHut3haAnvzWBerWVieGewjnOm7Jt2yrgnlYxw'}\n" + "cnf: {'jkt': '3RbQ7dAf2ynmHdDqLkepzb1aqd3vTbxNsIDSR5J4m4g'}\n" ] } ], @@ -379,7 +379,7 @@ }, { "cell_type": "markdown", - "id": "a2a5a837", + "id": "9fcda406", "metadata": {}, "source": [ "A replayed proof must be rejected — the `jti` ledger is atomic (ODIS's replay concern\n", @@ -389,13 +389,13 @@ { "cell_type": "code", "execution_count": 8, - "id": "0f4c024e", + "id": "d7500b9d", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:37:53.622163Z", - "iopub.status.busy": "2026-09-04T20:37:53.622084Z", - "iopub.status.idle": "2026-09-04T20:37:53.626669Z", - "shell.execute_reply": "2026-09-04T20:37:53.626315Z" + "iopub.execute_input": "2026-09-04T20:39:05.374510Z", + "iopub.status.busy": "2026-09-04T20:39:05.374432Z", + "iopub.status.idle": "2026-09-04T20:39:05.379260Z", + "shell.execute_reply": "2026-09-04T20:39:05.378822Z" } }, "outputs": [ @@ -420,7 +420,7 @@ }, { "cell_type": "markdown", - "id": "2f421145", + "id": "b378b462", "metadata": {}, "source": [ "## 6 · Delegation Record — Layer 2 · The Bridge (ODIS §6.3, Pillar 4, L2-01/05/06)\n", @@ -438,13 +438,13 @@ { "cell_type": "code", "execution_count": 9, - "id": "a906c986", + "id": "5792e1d6", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:37:53.627778Z", - "iopub.status.busy": "2026-09-04T20:37:53.627709Z", - "iopub.status.idle": "2026-09-04T20:37:53.647668Z", - "shell.execute_reply": "2026-09-04T20:37:53.647310Z" + "iopub.execute_input": "2026-09-04T20:39:05.380292Z", + "iopub.status.busy": "2026-09-04T20:39:05.380214Z", + "iopub.status.idle": "2026-09-04T20:39:05.403435Z", + "shell.execute_reply": "2026-09-04T20:39:05.402994Z" } }, "outputs": [ @@ -453,13 +453,13 @@ "output_type": "stream", "text": [ "{\n", - " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/researcher-e697bf\",\n", + " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/researcher-7f3724\",\n", " \"act\": {\n", - " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-e697bf\"\n", + " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-7f3724\"\n", " },\n", " \"delegation_depth\": 1,\n", - " \"mission_id\": \"e0e596a7-7877-4cd1-98d4-d0c91db46264\",\n", - " \"jti\": \"0151d06b-ce71-4711-8b07-671f6b9a535e\"\n", + " \"mission_id\": \"b1284566-711c-4dc5-bdd5-67321706fa80\",\n", + " \"jti\": \"aa0ed8d9-a672-4d2f-b871-e609cd5529aa\"\n", "}\n" ] } @@ -494,7 +494,7 @@ }, { "cell_type": "markdown", - "id": "c05483d7", + "id": "0431a562", "metadata": {}, "source": [ "Attenuation is **monotonic**: the researcher's policy ceiling is `data:read`, so a\n", @@ -507,13 +507,13 @@ { "cell_type": "code", "execution_count": 10, - "id": "7b59ec04", + "id": "6f59d60b", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:37:53.648621Z", - "iopub.status.busy": "2026-09-04T20:37:53.648552Z", - "iopub.status.idle": "2026-09-04T20:37:53.657633Z", - "shell.execute_reply": "2026-09-04T20:37:53.657152Z" + "iopub.execute_input": "2026-09-04T20:39:05.404437Z", + "iopub.status.busy": "2026-09-04T20:39:05.404359Z", + "iopub.status.idle": "2026-09-04T20:39:05.414693Z", + "shell.execute_reply": "2026-09-04T20:39:05.414245Z" } }, "outputs": [ @@ -547,7 +547,7 @@ }, { "cell_type": "markdown", - "id": "81a8278b", + "id": "ddb3ae4d", "metadata": {}, "source": [ "## 7 · Bounded asynchronous human authorization — Layer 2 · The Bridge (ODIS-L2-02, Pillar 1)\n", @@ -570,13 +570,13 @@ { "cell_type": "code", "execution_count": 11, - "id": "ad64095e", + "id": "4dd147ee", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:37:53.658623Z", - "iopub.status.busy": "2026-09-04T20:37:53.658551Z", - "iopub.status.idle": "2026-09-04T20:37:53.973891Z", - "shell.execute_reply": "2026-09-04T20:37:53.972964Z" + "iopub.execute_input": "2026-09-04T20:39:05.415734Z", + "iopub.status.busy": "2026-09-04T20:39:05.415645Z", + "iopub.status.idle": "2026-09-04T20:39:05.738163Z", + "shell.execute_reply": "2026-09-04T20:39:05.736987Z" } }, "outputs": [ @@ -586,7 +586,7 @@ "text": [ "bc-authorize: 200\n", "{\n", - " \"auth_req_id\": \"3zwLJHOC6kCxC6Rs53_ZGAK5I4dwgH8mOA4VVf8sg_4\",\n", + " \"auth_req_id\": \"e4VRTVp8hayx4YA8KMXdkzgofZaY8ZypErZI488jQps\",\n", " \"expires_in\": 300,\n", " \"interval\": 5\n", "}\n" @@ -640,7 +640,7 @@ }, { "cell_type": "markdown", - "id": "998ca7a2", + "id": "13b3e27a", "metadata": {}, "source": [ "## 8 · Confirmed compromise → cascade revocation — ODIS-L1-12, L3-04 / L3-05\n", @@ -654,13 +654,13 @@ { "cell_type": "code", "execution_count": 12, - "id": "9c5fc5de", + "id": "89746a0d", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:37:53.976582Z", - "iopub.status.busy": "2026-09-04T20:37:53.976286Z", - "iopub.status.idle": "2026-09-04T20:37:53.994878Z", - "shell.execute_reply": "2026-09-04T20:37:53.993986Z" + "iopub.execute_input": "2026-09-04T20:39:05.740534Z", + "iopub.status.busy": "2026-09-04T20:39:05.740323Z", + "iopub.status.idle": "2026-09-04T20:39:05.755386Z", + "shell.execute_reply": "2026-09-04T20:39:05.754327Z" } }, "outputs": [ @@ -668,7 +668,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "delegated token after cascade: {'active': False} (13 ms signal→dead)\n" + "delegated token after cascade: {'active': False} (10 ms signal→dead)\n" ] } ], @@ -684,7 +684,7 @@ }, { "cell_type": "markdown", - "id": "c3b9efdd", + "id": "e6cb2e03", "metadata": {}, "source": [ "## 9 · Audit lineage survives the kill — ODIS-CC-01 / CC-02\n", @@ -697,13 +697,13 @@ { "cell_type": "code", "execution_count": 13, - "id": "090e70c5", + "id": "9d9ed5b7", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:37:53.997732Z", - "iopub.status.busy": "2026-09-04T20:37:53.997475Z", - "iopub.status.idle": "2026-09-04T20:37:54.009042Z", - "shell.execute_reply": "2026-09-04T20:37:54.007640Z" + "iopub.execute_input": "2026-09-04T20:39:05.758168Z", + "iopub.status.busy": "2026-09-04T20:39:05.757944Z", + "iopub.status.idle": "2026-09-04T20:39:05.767317Z", + "shell.execute_reply": "2026-09-04T20:39:05.766693Z" } }, "outputs": [ @@ -711,7 +711,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "chain: orch-e697bf → researcher-e697bf\n", + "chain: orch-7f3724 → researcher-7f3724\n", "[\n", " {\n", " \"grant_type\": \"api_key\",\n", @@ -726,7 +726,7 @@ " ],\n", " \"attenuated\": [],\n", " \"is_revoked\": true,\n", - " \"revoke_reason\": \"auto-revoked by CAE signal 0b11ebf2-d1d8-4e2a-9a1b-9348b57a4d4c (severity: critical)\"\n", + " \"revoke_reason\": \"auto-revoked by CAE signal ce471e35-5118-444f-af3b-c5ee6488dbd2 (severity: critical)\"\n", " },\n", " {\n", " \"grant_type\": \"token_exchange\",\n", @@ -742,7 +742,7 @@ " \"data:write\"\n", " ],\n", " \"is_revoked\": true,\n", - " \"revoke_reason\": \"auto-revoked by CAE signal 0b11ebf2-d1d8-4e2a-9a1b-9348b57a4d4c (severity: critical)\"\n", + " \"revoke_reason\": \"auto-revoked by CAE signal ce471e35-5118-444f-af3b-c5ee6488dbd2 (severity: critical)\"\n", " }\n", "]\n" ] @@ -757,7 +757,7 @@ }, { "cell_type": "markdown", - "id": "45a7e934", + "id": "89903733", "metadata": {}, "source": [ "## 10 · The sponsor leaves — offboarding kill switch (ODIS-L1-06 / L1-10 / L3-05)\n", @@ -778,13 +778,13 @@ { "cell_type": "code", "execution_count": 14, - "id": "5bf2ac58", + "id": "c2d56136", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:37:54.011451Z", - "iopub.status.busy": "2026-09-04T20:37:54.011346Z", - "iopub.status.idle": "2026-09-04T20:37:54.031143Z", - "shell.execute_reply": "2026-09-04T20:37:54.030375Z" + "iopub.execute_input": "2026-09-04T20:39:05.769842Z", + "iopub.status.busy": "2026-09-04T20:39:05.769646Z", + "iopub.status.idle": "2026-09-04T20:39:05.791855Z", + "shell.execute_reply": "2026-09-04T20:39:05.790955Z" } }, "outputs": [ @@ -822,7 +822,7 @@ }, { "cell_type": "markdown", - "id": "3a7b11f0", + "id": "db78ed38", "metadata": {}, "source": [ "## What you just saw, in ODIS terms\n", diff --git a/examples/zeroid_quickstart.ipynb b/examples/zeroid_quickstart.ipynb index 16067274..821a16ed 100644 --- a/examples/zeroid_quickstart.ipynb +++ b/examples/zeroid_quickstart.ipynb @@ -80,7 +80,8 @@ "metadata": {}, "outputs": [], "source": [ - "#!pip install -q highflame cryptography PyJWT" + "# Uncomment the next line to install the prerequisites into this kernel:\n", + "# %pip install -q highflame cryptography PyJWT" ] }, { From 9e5cc53ced76726a6aa670d609934c8005e0006d Mon Sep 17 00:00:00 2001 From: Sharath Rajasekar Date: Fri, 4 Sep 2026 13:42:27 -0700 Subject: [PATCH 14/14] docs: notebook install cells are unquiet, --upgrade, and say to restart the kernel A quiet pip in a kernel with a conflicting pyjwt pin fails silently and the next cell's import then raises ModuleNotFoundError with no clue why. Show pip's output, upgrade explicitly, name PyJWT's real extra (crypto), and tell the reader to restart the kernel after installing. Co-Authored-By: Claude Fable 5 --- examples/odis/odis-walkthrough-sdk.ipynb | 122 +++++++------- examples/odis/odis-walkthrough.ipynb | 205 ++++++++++++----------- examples/zeroid_quickstart.ipynb | 5 +- 3 files changed, 168 insertions(+), 164 deletions(-) diff --git a/examples/odis/odis-walkthrough-sdk.ipynb b/examples/odis/odis-walkthrough-sdk.ipynb index f5ef2049..6e24b722 100644 --- a/examples/odis/odis-walkthrough-sdk.ipynb +++ b/examples/odis/odis-walkthrough-sdk.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "16774192", + "id": "48e274d2", "metadata": {}, "source": [ "# ZeroID as ODIS Layers 1–2 — the SDK companion\n", @@ -38,31 +38,33 @@ { "cell_type": "code", "execution_count": 1, - "id": "17f67afa", + "id": "0f530ece", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:39:06.522823Z", - "iopub.status.busy": "2026-09-04T20:39:06.522511Z", - "iopub.status.idle": "2026-09-04T20:39:06.528407Z", - "shell.execute_reply": "2026-09-04T20:39:06.527789Z" + "iopub.execute_input": "2026-09-04T20:42:25.133609Z", + "iopub.status.busy": "2026-09-04T20:42:25.133448Z", + "iopub.status.idle": "2026-09-04T20:42:25.137878Z", + "shell.execute_reply": "2026-09-04T20:42:25.137255Z" } }, "outputs": [], "source": [ - "# Uncomment the next line to install the prerequisites into this kernel (latest SDK release):\n", - "# %pip install -q highflame pyjwt cryptography" + "# Uncomment the next line to install the prerequisites into this kernel (latest SDK release),\n", + "# then restart the kernel before running the next cell. Left unquiet on purpose: if pip\n", + "# hits a dependency conflict in your environment you want to see it, not a silent no-op.\n", + "# %pip install --upgrade highflame \"PyJWT[crypto]>=2.13\" cryptography" ] }, { "cell_type": "code", "execution_count": 2, - "id": "6438ccd6", + "id": "6114ccb0", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:39:06.530838Z", - "iopub.status.busy": "2026-09-04T20:39:06.530662Z", - "iopub.status.idle": "2026-09-04T20:39:06.681320Z", - "shell.execute_reply": "2026-09-04T20:39:06.680844Z" + "iopub.execute_input": "2026-09-04T20:42:25.139831Z", + "iopub.status.busy": "2026-09-04T20:42:25.139662Z", + "iopub.status.idle": "2026-09-04T20:42:25.259279Z", + "shell.execute_reply": "2026-09-04T20:42:25.258776Z" } }, "outputs": [ @@ -70,7 +72,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "status='healthy' service='zeroid' timestamp=datetime.datetime(2026, 9, 4, 20, 39, 6, 673991, tzinfo=TzInfo(0)) uptime_ms=512550 | run id: bfc866\n" + "status='healthy' service='zeroid' timestamp=datetime.datetime(2026, 9, 4, 20, 42, 25, 255728, tzinfo=TzInfo(0)) uptime_ms=711126 | run id: 059ce3\n" ] } ], @@ -88,7 +90,7 @@ }, { "cell_type": "markdown", - "id": "49aad371", + "id": "d38bde89", "metadata": {}, "source": [ "## 1 · Agent Registration Record — Layer 1 · The Passport (ODIS §6.1)\n", @@ -104,13 +106,13 @@ { "cell_type": "code", "execution_count": 3, - "id": "882abba3", + "id": "7ee7c8fd", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:39:06.682765Z", - "iopub.status.busy": "2026-09-04T20:39:06.682647Z", - "iopub.status.idle": "2026-09-04T20:39:06.691940Z", - "shell.execute_reply": "2026-09-04T20:39:06.691593Z" + "iopub.execute_input": "2026-09-04T20:42:25.260508Z", + "iopub.status.busy": "2026-09-04T20:42:25.260419Z", + "iopub.status.idle": "2026-09-04T20:42:25.272167Z", + "shell.execute_reply": "2026-09-04T20:42:25.271806Z" } }, "outputs": [ @@ -118,7 +120,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-bfc866 | trust: unverified | status: active\n" + "spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-059ce3 | trust: unverified | status: active\n" ] } ], @@ -132,7 +134,7 @@ }, { "cell_type": "markdown", - "id": "14be9339", + "id": "a38f7014", "metadata": {}, "source": [ "## 2 · Agent Runtime Credential — the Passport's output (ODIS §6.2, L1-05)\n", @@ -144,13 +146,13 @@ { "cell_type": "code", "execution_count": 4, - "id": "896280e8", + "id": "88968c72", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:39:06.693263Z", - "iopub.status.busy": "2026-09-04T20:39:06.693168Z", - "iopub.status.idle": "2026-09-04T20:39:06.702504Z", - "shell.execute_reply": "2026-09-04T20:39:06.702077Z" + "iopub.execute_input": "2026-09-04T20:42:25.273401Z", + "iopub.status.busy": "2026-09-04T20:42:25.273329Z", + "iopub.status.idle": "2026-09-04T20:42:25.281299Z", + "shell.execute_reply": "2026-09-04T20:42:25.280879Z" } }, "outputs": [ @@ -169,7 +171,7 @@ }, { "cell_type": "markdown", - "id": "7b60cfc3", + "id": "9ba45386", "metadata": {}, "source": [ "## 3 · The ODIS-aware target — the Bridge's native mode (ODIS §2.5 / L2-15)\n", @@ -194,13 +196,13 @@ { "cell_type": "code", "execution_count": 5, - "id": "656c8182", + "id": "18052f25", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:39:06.703560Z", - "iopub.status.busy": "2026-09-04T20:39:06.703485Z", - "iopub.status.idle": "2026-09-04T20:39:06.709316Z", - "shell.execute_reply": "2026-09-04T20:39:06.708886Z" + "iopub.execute_input": "2026-09-04T20:42:25.282398Z", + "iopub.status.busy": "2026-09-04T20:42:25.282316Z", + "iopub.status.idle": "2026-09-04T20:42:25.288160Z", + "shell.execute_reply": "2026-09-04T20:42:25.287687Z" } }, "outputs": [ @@ -208,7 +210,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "verified: spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-bfc866 | delegated: False\n", + "verified: spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-059ce3 | delegated: False\n", "refused (ZeroIDError): Missing required scope: 'admin:all'\n", "refused (ZeroIDError): Insufficient trust level: required 'first_party', got 'unverified'\n" ] @@ -229,7 +231,7 @@ }, { "cell_type": "markdown", - "id": "23178d92", + "id": "801f3872", "metadata": {}, "source": [ "Audience is the first duty on ODIS §2.5's native-mode list, and it is one argument\n", @@ -241,13 +243,13 @@ { "cell_type": "code", "execution_count": 6, - "id": "34ff0abe", + "id": "b720f3f2", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:39:06.710444Z", - "iopub.status.busy": "2026-09-04T20:39:06.710362Z", - "iopub.status.idle": "2026-09-04T20:39:06.713113Z", - "shell.execute_reply": "2026-09-04T20:39:06.712677Z" + "iopub.execute_input": "2026-09-04T20:42:25.289147Z", + "iopub.status.busy": "2026-09-04T20:42:25.289079Z", + "iopub.status.idle": "2026-09-04T20:42:25.291793Z", + "shell.execute_reply": "2026-09-04T20:42:25.291323Z" } }, "outputs": [ @@ -272,7 +274,7 @@ }, { "cell_type": "markdown", - "id": "0e876a51", + "id": "a08ef8f0", "metadata": {}, "source": [ "Trust is **minted into the credential at issuance** — `trust_level` is a claim. So\n", @@ -288,13 +290,13 @@ { "cell_type": "code", "execution_count": 7, - "id": "5dbdf0d5", + "id": "6e1b56cd", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:39:06.714201Z", - "iopub.status.busy": "2026-09-04T20:39:06.714134Z", - "iopub.status.idle": "2026-09-04T20:39:06.725683Z", - "shell.execute_reply": "2026-09-04T20:39:06.725248Z" + "iopub.execute_input": "2026-09-04T20:42:25.292833Z", + "iopub.status.busy": "2026-09-04T20:42:25.292768Z", + "iopub.status.idle": "2026-09-04T20:42:25.302926Z", + "shell.execute_reply": "2026-09-04T20:42:25.302496Z" } }, "outputs": [ @@ -316,7 +318,7 @@ }, { "cell_type": "markdown", - "id": "989cb701", + "id": "6f76f7fc", "metadata": {}, "source": [ "## 4 · Delegation Record — Layer 2 · The Bridge (ODIS §6.3, Pillar 4, L2-05/06)\n", @@ -333,13 +335,13 @@ { "cell_type": "code", "execution_count": 8, - "id": "f542bb9d", + "id": "5e769b33", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:39:06.726777Z", - "iopub.status.busy": "2026-09-04T20:39:06.726690Z", - "iopub.status.idle": "2026-09-04T20:39:06.742734Z", - "shell.execute_reply": "2026-09-04T20:39:06.742296Z" + "iopub.execute_input": "2026-09-04T20:42:25.303920Z", + "iopub.status.busy": "2026-09-04T20:42:25.303847Z", + "iopub.status.idle": "2026-09-04T20:42:25.320697Z", + "shell.execute_reply": "2026-09-04T20:42:25.320280Z" } }, "outputs": [ @@ -348,7 +350,7 @@ "output_type": "stream", "text": [ "granted scope (requested read+write): 'data:read'\n", - "sub: researcher-bfc866 | is_delegated: True | act: {'sub': 'spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-bfc866'} | depth: 1\n", + "sub: researcher-059ce3 | is_delegated: True | act: {'sub': 'spiffe://highflame.ai/acct-sdk-demo/proj-sdk-demo/agent/orch-059ce3'} | depth: 1\n", "empty intersection refused (APIError): [400] invalid_scope: requested scopes are not available for delegation\n" ] } @@ -388,7 +390,7 @@ }, { "cell_type": "markdown", - "id": "e659a8c7", + "id": "a42a9ba3", "metadata": {}, "source": [ "## 5 · Compromise signal, cascade — and what native mode must add — L1-12, L3-04/05\n", @@ -405,13 +407,13 @@ { "cell_type": "code", "execution_count": 9, - "id": "d29d16fc", + "id": "79b3f362", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:39:06.743802Z", - "iopub.status.busy": "2026-09-04T20:39:06.743724Z", - "iopub.status.idle": "2026-09-04T20:39:06.749458Z", - "shell.execute_reply": "2026-09-04T20:39:06.749096Z" + "iopub.execute_input": "2026-09-04T20:42:25.321770Z", + "iopub.status.busy": "2026-09-04T20:42:25.321685Z", + "iopub.status.idle": "2026-09-04T20:42:25.328331Z", + "shell.execute_reply": "2026-09-04T20:42:25.327943Z" } }, "outputs": [ @@ -436,7 +438,7 @@ }, { "cell_type": "markdown", - "id": "babc69f1", + "id": "3c3559ba", "metadata": {}, "source": [ "## What the SDK view added\n", diff --git a/examples/odis/odis-walkthrough.ipynb b/examples/odis/odis-walkthrough.ipynb index fec494d9..1d05370e 100644 --- a/examples/odis/odis-walkthrough.ipynb +++ b/examples/odis/odis-walkthrough.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "b826f0a7", + "id": "a93c50eb", "metadata": {}, "source": [ "# ZeroID as ODIS Layers 1–2 — an executable walkthrough\n", @@ -48,31 +48,32 @@ { "cell_type": "code", "execution_count": 1, - "id": "a8bebe81", + "id": "bd90f802", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:39:05.219781Z", - "iopub.status.busy": "2026-09-04T20:39:05.219541Z", - "iopub.status.idle": "2026-09-04T20:39:05.226207Z", - "shell.execute_reply": "2026-09-04T20:39:05.225198Z" + "iopub.execute_input": "2026-09-04T20:42:23.873460Z", + "iopub.status.busy": "2026-09-04T20:42:23.873323Z", + "iopub.status.idle": "2026-09-04T20:42:23.877178Z", + "shell.execute_reply": "2026-09-04T20:42:23.876596Z" } }, "outputs": [], "source": [ - "# Uncomment the next line to install the prerequisites into this kernel:\n", - "# %pip install -q requests pyjwt cryptography" + "# Uncomment the next line to install the prerequisites into this kernel, then restart the\n", + "# kernel before running the next cell.\n", + "# %pip install --upgrade requests \"PyJWT[crypto]>=2.8\" cryptography" ] }, { "cell_type": "code", "execution_count": 2, - "id": "9447d1e4", + "id": "3e8afe30", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:39:05.228665Z", - "iopub.status.busy": "2026-09-04T20:39:05.228429Z", - "iopub.status.idle": "2026-09-04T20:39:05.308155Z", - "shell.execute_reply": "2026-09-04T20:39:05.307581Z" + "iopub.execute_input": "2026-09-04T20:42:23.878597Z", + "iopub.status.busy": "2026-09-04T20:42:23.878499Z", + "iopub.status.idle": "2026-09-04T20:42:23.939201Z", + "shell.execute_reply": "2026-09-04T20:42:23.938690Z" } }, "outputs": [ @@ -80,7 +81,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "zeroid: healthy | run id: 7f3724\n" + "zeroid: healthy | run id: ba88ca\n" ] } ], @@ -118,7 +119,7 @@ }, { "cell_type": "markdown", - "id": "e71dc210", + "id": "535625b0", "metadata": {}, "source": [ "## 1 · Agent Registration Record — Layer 1 · The Passport (ODIS §6.1)\n", @@ -133,13 +134,13 @@ { "cell_type": "code", "execution_count": 3, - "id": "b4c42f75", + "id": "17ac8435", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:39:05.309320Z", - "iopub.status.busy": "2026-09-04T20:39:05.309209Z", - "iopub.status.idle": "2026-09-04T20:39:05.322027Z", - "shell.execute_reply": "2026-09-04T20:39:05.321457Z" + "iopub.execute_input": "2026-09-04T20:42:23.940405Z", + "iopub.status.busy": "2026-09-04T20:42:23.940314Z", + "iopub.status.idle": "2026-09-04T20:42:23.956421Z", + "shell.execute_reply": "2026-09-04T20:42:23.955909Z" } }, "outputs": [ @@ -148,7 +149,7 @@ "output_type": "stream", "text": [ "{\n", - " \"wimse_uri\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-7f3724\",\n", + " \"wimse_uri\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-ba88ca\",\n", " \"identity_type\": \"agent\",\n", " \"trust_level\": \"unverified\",\n", " \"status\": \"active\",\n", @@ -171,7 +172,7 @@ }, { "cell_type": "markdown", - "id": "0fcc2db8", + "id": "53fa9282", "metadata": {}, "source": [ "## 2 · Registration-record governance fields — Layer 1 · The Passport (ODIS §6.1)\n", @@ -187,13 +188,13 @@ { "cell_type": "code", "execution_count": 4, - "id": "a5733d80", + "id": "0eea94fd", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:39:05.323307Z", - "iopub.status.busy": "2026-09-04T20:39:05.323219Z", - "iopub.status.idle": "2026-09-04T20:39:05.334554Z", - "shell.execute_reply": "2026-09-04T20:39:05.334098Z" + "iopub.execute_input": "2026-09-04T20:42:23.957494Z", + "iopub.status.busy": "2026-09-04T20:42:23.957419Z", + "iopub.status.idle": "2026-09-04T20:42:23.968969Z", + "shell.execute_reply": "2026-09-04T20:42:23.968504Z" } }, "outputs": [ @@ -202,7 +203,7 @@ "output_type": "stream", "text": [ "{\n", - " \"name\": \"odis-demo-7f3724\",\n", + " \"name\": \"odis-demo-ba88ca\",\n", " \"max_ttl_seconds\": 3600,\n", " \"required_trust_level\": \"first_party\",\n", " \"max_delegation_depth\": 2\n", @@ -226,7 +227,7 @@ }, { "cell_type": "markdown", - "id": "66a5e853", + "id": "5b08f6ea", "metadata": {}, "source": [ "## 3 · No registration resolution, no authority — The Bridge refuses (ODIS-L2-14 + L1-11)\n", @@ -239,13 +240,13 @@ { "cell_type": "code", "execution_count": 5, - "id": "4aec1151", + "id": "217bd311", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:39:05.335701Z", - "iopub.status.busy": "2026-09-04T20:39:05.335608Z", - "iopub.status.idle": "2026-09-04T20:39:05.342892Z", - "shell.execute_reply": "2026-09-04T20:39:05.342432Z" + "iopub.execute_input": "2026-09-04T20:42:23.970168Z", + "iopub.status.busy": "2026-09-04T20:42:23.970081Z", + "iopub.status.idle": "2026-09-04T20:42:23.976148Z", + "shell.execute_reply": "2026-09-04T20:42:23.975681Z" } }, "outputs": [ @@ -269,7 +270,7 @@ }, { "cell_type": "markdown", - "id": "cc6dbe99", + "id": "7d8b87d1", "metadata": {}, "source": [ "## 4 · Attestation raises trust — Layer 1 · The Passport (ODIS-L1-03 / L1-11)\n", @@ -286,13 +287,13 @@ { "cell_type": "code", "execution_count": 6, - "id": "92c7c238", + "id": "20b177cc", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:39:05.344187Z", - "iopub.status.busy": "2026-09-04T20:39:05.344085Z", - "iopub.status.idle": "2026-09-04T20:39:05.360712Z", - "shell.execute_reply": "2026-09-04T20:39:05.360212Z" + "iopub.execute_input": "2026-09-04T20:42:23.977223Z", + "iopub.status.busy": "2026-09-04T20:42:23.977148Z", + "iopub.status.idle": "2026-09-04T20:42:23.990895Z", + "shell.execute_reply": "2026-09-04T20:42:23.990414Z" } }, "outputs": [ @@ -317,7 +318,7 @@ }, { "cell_type": "markdown", - "id": "050225c6", + "id": "52d1d7a1", "metadata": {}, "source": [ "## 5 · Agent Runtime Credential — the Passport's output (ODIS §6.2, L1-05 / L1-09)\n", @@ -338,13 +339,13 @@ { "cell_type": "code", "execution_count": 7, - "id": "32440c89", + "id": "b6d02473", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:39:05.361708Z", - "iopub.status.busy": "2026-09-04T20:39:05.361631Z", - "iopub.status.idle": "2026-09-04T20:39:05.373473Z", - "shell.execute_reply": "2026-09-04T20:39:05.373031Z" + "iopub.execute_input": "2026-09-04T20:42:23.992007Z", + "iopub.status.busy": "2026-09-04T20:42:23.991931Z", + "iopub.status.idle": "2026-09-04T20:42:24.003875Z", + "shell.execute_reply": "2026-09-04T20:42:24.003432Z" } }, "outputs": [ @@ -354,12 +355,12 @@ "text": [ "token_type: DPoP | expires_in: 3600 s\n", "{\n", - " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-7f3724\",\n", + " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-ba88ca\",\n", " \"trust_level\": \"first_party\",\n", - " \"mission_id\": \"b1284566-711c-4dc5-bdd5-67321706fa80\",\n", - " \"jti\": \"b1284566-711c-4dc5-bdd5-67321706fa80\"\n", + " \"mission_id\": \"deddd91a-cdee-4d14-8f37-7e2130698154\",\n", + " \"jti\": \"deddd91a-cdee-4d14-8f37-7e2130698154\"\n", "}\n", - "cnf: {'jkt': '3RbQ7dAf2ynmHdDqLkepzb1aqd3vTbxNsIDSR5J4m4g'}\n" + "cnf: {'jkt': 'j-q4OkwXHy0F2KAwkbBpSjWX7I0JX2MYRljci5AhTmg'}\n" ] } ], @@ -379,7 +380,7 @@ }, { "cell_type": "markdown", - "id": "9fcda406", + "id": "4bd09e14", "metadata": {}, "source": [ "A replayed proof must be rejected — the `jti` ledger is atomic (ODIS's replay concern\n", @@ -389,13 +390,13 @@ { "cell_type": "code", "execution_count": 8, - "id": "d7500b9d", + "id": "9d11293e", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:39:05.374510Z", - "iopub.status.busy": "2026-09-04T20:39:05.374432Z", - "iopub.status.idle": "2026-09-04T20:39:05.379260Z", - "shell.execute_reply": "2026-09-04T20:39:05.378822Z" + "iopub.execute_input": "2026-09-04T20:42:24.004950Z", + "iopub.status.busy": "2026-09-04T20:42:24.004866Z", + "iopub.status.idle": "2026-09-04T20:42:24.009376Z", + "shell.execute_reply": "2026-09-04T20:42:24.008960Z" } }, "outputs": [ @@ -420,7 +421,7 @@ }, { "cell_type": "markdown", - "id": "b378b462", + "id": "a91ec435", "metadata": {}, "source": [ "## 6 · Delegation Record — Layer 2 · The Bridge (ODIS §6.3, Pillar 4, L2-01/05/06)\n", @@ -438,13 +439,13 @@ { "cell_type": "code", "execution_count": 9, - "id": "5792e1d6", + "id": "136badea", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:39:05.380292Z", - "iopub.status.busy": "2026-09-04T20:39:05.380214Z", - "iopub.status.idle": "2026-09-04T20:39:05.403435Z", - "shell.execute_reply": "2026-09-04T20:39:05.402994Z" + "iopub.execute_input": "2026-09-04T20:42:24.010522Z", + "iopub.status.busy": "2026-09-04T20:42:24.010445Z", + "iopub.status.idle": "2026-09-04T20:42:24.033710Z", + "shell.execute_reply": "2026-09-04T20:42:24.033347Z" } }, "outputs": [ @@ -453,13 +454,13 @@ "output_type": "stream", "text": [ "{\n", - " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/researcher-7f3724\",\n", + " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/researcher-ba88ca\",\n", " \"act\": {\n", - " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-7f3724\"\n", + " \"sub\": \"spiffe://highflame.ai/acct-demo/proj-demo/agent/orch-ba88ca\"\n", " },\n", " \"delegation_depth\": 1,\n", - " \"mission_id\": \"b1284566-711c-4dc5-bdd5-67321706fa80\",\n", - " \"jti\": \"aa0ed8d9-a672-4d2f-b871-e609cd5529aa\"\n", + " \"mission_id\": \"deddd91a-cdee-4d14-8f37-7e2130698154\",\n", + " \"jti\": \"2f8712b1-a479-48c7-9b69-3cc3e3d1946d\"\n", "}\n" ] } @@ -494,7 +495,7 @@ }, { "cell_type": "markdown", - "id": "0431a562", + "id": "e6a68996", "metadata": {}, "source": [ "Attenuation is **monotonic**: the researcher's policy ceiling is `data:read`, so a\n", @@ -507,13 +508,13 @@ { "cell_type": "code", "execution_count": 10, - "id": "6f59d60b", + "id": "ab96d0d3", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:39:05.404437Z", - "iopub.status.busy": "2026-09-04T20:39:05.404359Z", - "iopub.status.idle": "2026-09-04T20:39:05.414693Z", - "shell.execute_reply": "2026-09-04T20:39:05.414245Z" + "iopub.execute_input": "2026-09-04T20:42:24.034847Z", + "iopub.status.busy": "2026-09-04T20:42:24.034766Z", + "iopub.status.idle": "2026-09-04T20:42:24.043674Z", + "shell.execute_reply": "2026-09-04T20:42:24.043287Z" } }, "outputs": [ @@ -547,7 +548,7 @@ }, { "cell_type": "markdown", - "id": "ddb3ae4d", + "id": "3715ffae", "metadata": {}, "source": [ "## 7 · Bounded asynchronous human authorization — Layer 2 · The Bridge (ODIS-L2-02, Pillar 1)\n", @@ -570,13 +571,13 @@ { "cell_type": "code", "execution_count": 11, - "id": "4dd147ee", + "id": "069d9b03", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:39:05.415734Z", - "iopub.status.busy": "2026-09-04T20:39:05.415645Z", - "iopub.status.idle": "2026-09-04T20:39:05.738163Z", - "shell.execute_reply": "2026-09-04T20:39:05.736987Z" + "iopub.execute_input": "2026-09-04T20:42:24.044864Z", + "iopub.status.busy": "2026-09-04T20:42:24.044786Z", + "iopub.status.idle": "2026-09-04T20:42:24.361724Z", + "shell.execute_reply": "2026-09-04T20:42:24.360784Z" } }, "outputs": [ @@ -586,7 +587,7 @@ "text": [ "bc-authorize: 200\n", "{\n", - " \"auth_req_id\": \"e4VRTVp8hayx4YA8KMXdkzgofZaY8ZypErZI488jQps\",\n", + " \"auth_req_id\": \"JBmzNWs_GBYuG96OzPI_OABbG1tgSfkIrKsGUASgJMk\",\n", " \"expires_in\": 300,\n", " \"interval\": 5\n", "}\n" @@ -640,7 +641,7 @@ }, { "cell_type": "markdown", - "id": "13b3e27a", + "id": "dd539e45", "metadata": {}, "source": [ "## 8 · Confirmed compromise → cascade revocation — ODIS-L1-12, L3-04 / L3-05\n", @@ -654,13 +655,13 @@ { "cell_type": "code", "execution_count": 12, - "id": "89746a0d", + "id": "a6038f03", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:39:05.740534Z", - "iopub.status.busy": "2026-09-04T20:39:05.740323Z", - "iopub.status.idle": "2026-09-04T20:39:05.755386Z", - "shell.execute_reply": "2026-09-04T20:39:05.754327Z" + "iopub.execute_input": "2026-09-04T20:42:24.363860Z", + "iopub.status.busy": "2026-09-04T20:42:24.363709Z", + "iopub.status.idle": "2026-09-04T20:42:24.374309Z", + "shell.execute_reply": "2026-09-04T20:42:24.373839Z" } }, "outputs": [ @@ -668,7 +669,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "delegated token after cascade: {'active': False} (10 ms signal→dead)\n" + "delegated token after cascade: {'active': False} (8 ms signal→dead)\n" ] } ], @@ -684,7 +685,7 @@ }, { "cell_type": "markdown", - "id": "e6cb2e03", + "id": "c3d92a70", "metadata": {}, "source": [ "## 9 · Audit lineage survives the kill — ODIS-CC-01 / CC-02\n", @@ -697,13 +698,13 @@ { "cell_type": "code", "execution_count": 13, - "id": "9d9ed5b7", + "id": "72bd5ca1", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:39:05.758168Z", - "iopub.status.busy": "2026-09-04T20:39:05.757944Z", - "iopub.status.idle": "2026-09-04T20:39:05.767317Z", - "shell.execute_reply": "2026-09-04T20:39:05.766693Z" + "iopub.execute_input": "2026-09-04T20:42:24.375837Z", + "iopub.status.busy": "2026-09-04T20:42:24.375711Z", + "iopub.status.idle": "2026-09-04T20:42:24.381511Z", + "shell.execute_reply": "2026-09-04T20:42:24.381205Z" } }, "outputs": [ @@ -711,7 +712,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "chain: orch-7f3724 → researcher-7f3724\n", + "chain: orch-ba88ca → researcher-ba88ca\n", "[\n", " {\n", " \"grant_type\": \"api_key\",\n", @@ -726,7 +727,7 @@ " ],\n", " \"attenuated\": [],\n", " \"is_revoked\": true,\n", - " \"revoke_reason\": \"auto-revoked by CAE signal ce471e35-5118-444f-af3b-c5ee6488dbd2 (severity: critical)\"\n", + " \"revoke_reason\": \"auto-revoked by CAE signal a6978e71-8487-4e8a-b2f2-c633f37ed276 (severity: critical)\"\n", " },\n", " {\n", " \"grant_type\": \"token_exchange\",\n", @@ -742,7 +743,7 @@ " \"data:write\"\n", " ],\n", " \"is_revoked\": true,\n", - " \"revoke_reason\": \"auto-revoked by CAE signal ce471e35-5118-444f-af3b-c5ee6488dbd2 (severity: critical)\"\n", + " \"revoke_reason\": \"auto-revoked by CAE signal a6978e71-8487-4e8a-b2f2-c633f37ed276 (severity: critical)\"\n", " }\n", "]\n" ] @@ -757,7 +758,7 @@ }, { "cell_type": "markdown", - "id": "89903733", + "id": "87498388", "metadata": {}, "source": [ "## 10 · The sponsor leaves — offboarding kill switch (ODIS-L1-06 / L1-10 / L3-05)\n", @@ -778,13 +779,13 @@ { "cell_type": "code", "execution_count": 14, - "id": "c2d56136", + "id": "0dbc20ec", "metadata": { "execution": { - "iopub.execute_input": "2026-09-04T20:39:05.769842Z", - "iopub.status.busy": "2026-09-04T20:39:05.769646Z", - "iopub.status.idle": "2026-09-04T20:39:05.791855Z", - "shell.execute_reply": "2026-09-04T20:39:05.790955Z" + "iopub.execute_input": "2026-09-04T20:42:24.383167Z", + "iopub.status.busy": "2026-09-04T20:42:24.383066Z", + "iopub.status.idle": "2026-09-04T20:42:24.398210Z", + "shell.execute_reply": "2026-09-04T20:42:24.397666Z" } }, "outputs": [ @@ -822,7 +823,7 @@ }, { "cell_type": "markdown", - "id": "db78ed38", + "id": "44b3c44c", "metadata": {}, "source": [ "## What you just saw, in ODIS terms\n", diff --git a/examples/zeroid_quickstart.ipynb b/examples/zeroid_quickstart.ipynb index 821a16ed..c0ed3e0a 100644 --- a/examples/zeroid_quickstart.ipynb +++ b/examples/zeroid_quickstart.ipynb @@ -80,8 +80,9 @@ "metadata": {}, "outputs": [], "source": [ - "# Uncomment the next line to install the prerequisites into this kernel:\n", - "# %pip install -q highflame cryptography PyJWT" + "# Uncomment the next line to install the prerequisites into this kernel (latest SDK\n", + "# release), then restart the kernel before running the next cell.\n", + "# %pip install --upgrade highflame \"PyJWT[crypto]>=2.13\" cryptography" ] }, {