Skip to main content

HTTP, MCP, the human pages and the CLI

The Tines monitoring guide documents the separate source-authenticated HTTP endpoint, ADMIN pages, operator commands, local worker/receiver, tests and current delivery limits.

This reference describes the current private gateway, mock-provider and CLI interfaces. Source contracts live in requests.py, runtime.py and surfaces/. The app disables /docs, /redoc and the OpenAPI URL. There is no public policy-administration or replay API.

Application modes and authentication

The factory is execbound.app:create_app. A normal launch with only DATABASE_URL serves health and identity. Setting EXECBOUND_RUNTIME_CONFIG to the private gateway JSON composes execution, MCP and human surfaces. The validation harness sets up the complete mode for you.

Gateway API credentials are opaque tokens, presented as exactly one Authorization: Bearer <token> header. They are checked against retained digests and current expiry, revocation, principal/tenant status. Tokens are not Supabase JWTs. Execution/status require AGENT; human pages require a separately established human session.

Do not send tenant, principal, role or account override headers, extra query parameters, compressed bodies, duplicate security headers or hidden identity fields. The configured human origin also determines the accepted Host header. Errors are deliberately bounded and do not expose raw provider/SQL diagnostics.

Gateway HTTP routes

Method and pathAuthorityBehavior
GET /health/liveNone{"status":"ok"}; process liveness
GET /health/readyNone{"status":"ready","version":"<package version>","commit":"<source commit or null>"} after database revision/grant/RLS checks; commit comes from EXECBOUND_COMMIT (set at image build) or the platform's own RENDER_GIT_COMMIT, and is null when neither states a commit id; a gateway started from a private configuration adds keys (current, refused, failed or disabled), the outcome of the last key reload check; does not probe providers
GET /v1/meAny current gateway credentialtenant_id, principal_id, credential_id, kind, expires_at
POST /v1/executeAGENTStrict action body; executes, denies or returns pending/uncertain state through shared runtime. For an externally executed operation it answers a checkpoint object instead of dispatching (External execution checkpoint)
GET /v1/executions/{execution_id}AGENT owning the executionScoped status; optional X-Execution-Continuation header. Never carries a checkpoint: only the admission that issued a grant describes one
POST /v1/executions/{execution_id}/outcomeAGENT holding the grant's report secretAn external executor's report of what it did with its grant; settles the execution at the tier the evidence supports
POST /preAGENTPresent only when the private configuration carries an arcade block. A control point's Pre-Execution hook, answered OK, CHECK_FAILED or RATE_LIMIT_EXCEEDED (Arcade logic extension)
POST /postAGENTPresent under the same condition. The Post-Execution hook; settles the correlated execution where the report states a result, and answers OK for anything it can decide

A deployment with no arcade block serves exactly the rows above it, which is what says the hook surface is absent unless someone configured it; tests/test_route_authority.py asserts the served set in both constructions. The two hook routes take the same request discipline as the agent routes — no query parameters, none of the four identity headers, no content-encoding, no duplicated content-type or content-length, and a non-AGENT principal refused and audited — and a different body rule: the monitoring intake's, which is application/json and application/json; charset=utf-8 compared case-insensitively and nothing else, each route under its own byte bound (16 KiB for /pre, the configured post_bytes for /post). surfaces/http.read_body and what the agent routes accept do not change.

Status IDs use canonical lowercase UUID text. Status and identity have no request body. Execution content type is exactly application/json; the original body is capped at 16 KiB and read within five seconds. Strings, numbers and unknown fields are validated strictly; duplicate JSON keys, floats and nonfinite numbers are rejected.

Action request

This illustrative endpoint request uses a demo mapping name. The selected resource must actually exist in the authenticated tenant's registry; use the fixture helper below for a runnable request.

{
"operation_ref": "endpoint.isolate",
"target": {"kind": "hostname", "value": "workstation.example.test"},
"arguments": {},
"idempotency_key": "review-one",
"claimed_context": {"incident_id": "11111111-1111-4111-8111-111111111111"}
}

An action that declares arguments must carry exactly them. endpoint.lift_isolation takes one enumerated reason:

{
"operation_ref": "endpoint.lift_isolation",
"target": {"kind": "hostname", "value": "workstation.example.test"},
"arguments": {"reason": "false_positive"},
"idempotency_key": "review-two",
"claimed_context": {"incident_id": "11111111-1111-4111-8111-111111111111"}
}
FieldContract
operation_refRequired, 1–80 characters, pattern ^[a-z][a-z0-9_.-]{0,79}$; tenant mapping reference, not an arbitrary provider operation
target.kindcanonical, hostname or provider_id; identity actions reject hostname selectors
target.valueCanonical UUID; bounded ASCII DNS hostname normalized to lowercase without final dot; or case-sensitive provider ID matching [A-Za-z0-9._:-]{1,128}
argumentsRequired object of 0 to 16 keys matching ^[a-z][a-z0-9_]{0,62}$, each holding a string of 1–254 characters, a signed 64-bit integer or a boolean; no nesting, lists, nulls, floats or coercion. Resolution then requires exactly the resolved action's declared arguments, types and value domains (Action catalog); send {} for an action that declares none
idempotency_key1–128 characters from letters, digits, ., _, :, -; scoped to tenant/principal. Exactly one of idempotency_key and reference is required; a caller with its own key management sends the key
referenceThe ticket, case or run id this action responds to (1–200 characters, stripped). The gateway derives the idempotency key from it byte for byte as the Python client's intent_key does (the slug of the reference plus the first 32 hex characters of SHA-256 over the JSON list of operation, target kind, target value and reference), so a model-driven host and a Python client given the same ticket land on the same intent. A key beside a reference is refused unless it is the derived one. The Tines governed-action story sends a reference and no key for this reason, so it lands on the same intent as a Python client given the same ticket
run_idOptional UUID or null; must resolve to a valid server-issued run belonging to this principal
claimed_contextOptional strict object; untrusted claims described below; defaults to empty

The parser permits an absent claimed_context, but all current action paths require claimed_context.incident_id to select an independently verified incident linked to the resolved resource. The illustrative UUID above must be replaced with a real incident selector from the authenticated tenant's fixtures. It supplies a reference, not trusted severity or authority; an absent, foreign, stale or unrelated incident fails resolution.

Other optional claims are severity (LOW, MEDIUM, HIGH, CRITICAL), environment (development, staging, production), role (workstation, server, domain_controller) and integer criticality (0–4). All claim fields are nullable at the parser boundary. Their values never replace independent inventory/incident facts or assign authority.

The canonical selector contains the registry's account resource UUID (resource_id). Resolution then maps that representation to canonical_target_id for shared claims. Do not assume these UUIDs are interchangeable.

Execution response and retries

All successful execution/status payloads contain these fields; approval fields may be null:

FieldMeaning
execution_idDurable scoped execution UUID
stateOne of the execution states
plan_hash64 lowercase hexadecimal characters binding the frozen plan
approval_idAssociated approval UUID or null
approval_stateCurrent approval lifecycle state or null
continuationOptional signed status metadata for a pending approval
approval_urlOptional configured human approval page for pending work
observedOnly on an OBSERVED view: effect, reasons, obligation_ids and mode (tenant or mapping), the decision the kernel would have made for an observing tenant; a record, never authorization
checkpointOnly on the admission that granted or re-granted an externally executed operation: exactly {"grant": …, "act": …}

The checkpoint object

An operation whose owner-provisioned mapping declares an external executor is answered with a grant rather than dispatched. The object has exactly two keys, and they are different kinds of thing: grant is what admission committed and retained, and act is a token the presenter mints for this one answer and re-mints on every later one. act is always present as a key and is null when the deployment holds no continuation keys or the act window has already closed, because the reservation and the claim are committed either way and the caller must be told what it is holding.

{
"checkpoint": {
"grant": {
"operation_id": "…", "provider": "mock_crowdstrike", "provider_operation": "contain",
"resource_provider_id": "device-01", "action": "endpoint.isolate", "arguments": {},
"precondition_class": "UNAVAILABLE", "expected_version": null,
"act_expires_at": "…", "report_expires_at": "…",
"regranted": false, "report_secret": "…"
},
"act": "<signed continuation, or null>"
}
}

expected_version is present exactly when precondition_class is not UNAVAILABLE. report_secret is returned once, on the first answer only, and is null on a re-grant, which sets regranted to true; the row retains only its digest, so a caller that lost the first response has no re-mint. act is a continuation at the execution.checkpoint_outcome audience binding tenant, principal, credential, execution, operation, plan hash and expiry; nothing in ExecBound verifies that audience today, and the outcome route does not accept it. A grant is neither an execution nor an approval: ExecBound dispatched nothing, and approval_id and approval_state are null unless the same execution went through an approval.

The outcome report

{
"report_secret": "<the secret the first grant returned>",
"outcome": "EXECUTED",
"reason": "EXECUTED",
"record": {"version": "…", "key_id": "…", "record": {}, "signature": "…"}
}

outcome is EXECUTED, FAILED_NOT_EXECUTED, FAILED_EFFECT_UNKNOWN or UNKNOWN. reason is EXECUTED, VERSION_MISMATCH, CANCELLED_NOT_EXECUTED, TARGET_NOT_FOUND or REPORTED_FAILURE, and is absent for UNKNOWN. Outcome and reason are paired by a table rather than checked for agreement: each reason names exactly one outcome, REPORTED_FAILURE names FAILED_EFFECT_UNKNOWN and nothing else, and every other spelling is INVALID_REQUEST at the parser before an execution row is read. FAILED_EFFECT_UNKNOWN and REPORTED_FAILURE are outside the verified vocabulary — no signed executor record and no provider journal admits either — so a report presenting record beside REPORTED_FAILURE is refused at the parser too. Exactly one of record (the executor's Ed25519-signed envelope) and reported_at (its own clock, retained as a reported time and never a confirmation time) is carried. Unknown fields are refused, so an act grant cannot be presented here as a second authenticator. The refusals are INVALID_REQUEST and INVALID_RECORD (400), INVALID_REPORT (403), NOT_FOUND (404), INVALID_STATE, REPORT_WINDOW_CLOSED and INVALID_KEY_RING (409). What each outcome settles, and what a settled row still accepts, is in External execution checkpoint.

PENDING_APPROVAL, DISPATCH_COMMITTED and INDETERMINATE return 202. Other views return 200, including a policy DENIED view, an OBSERVED view, which records what an observing tenant would have decided and carries no continuation or approval page, and a settled FAILED_EFFECT_UNKNOWN view. A 2xx response therefore does not necessarily mean a provider effect occurred. Terminal states are DENIED, EXECUTED, FAILED_NOT_EXECUTED, FAILED_EFFECT_UNKNOWN, STALE_AUTHORIZATION, OBSERVED; ATTEMPTED/RESERVED are internal nonterminal stages.

Retry the same request/key to retrieve or progress the same intent after approval. The approval deadline is the earlier of the tenant's configured deadline after creation (120 seconds unless an ADMIN set another value between 120 and 3,600, or turned it off) and the frozen context expiry; both human decision and subsequent agent retry must precede it, and with the deadline off the context expiry alone applies. Status reads never dispatch. A changed intent under the same key conflicts. Equivalent unresolved intent cannot bypass ownership with a new key, alias, run or protocol; a new key after completed work can represent a new consequential action and consume new impact. Never use this as uncertain-operation recovery.

Pending status continuations are bounded by approval expiry, credential expiry and two minutes from issue; under a longer tenant deadline a client renews one by resending the same request, which returns the same pending execution with a fresh continuation. They bind tenant, principal, credential, audience, execution/approval IDs, plan and nonce. They supplement current authentication and are not execution/approval authority. Internal tickets and provider proof/secrets are not returned.

HTTP statusTypical meaning
400Invalid/ambiguous input, malformed identifier or continuation
401Missing/invalid/expired/revoked authentication
403Wrong principal kind or human authority
404Execution absent from the caller's scope
409IDEMPOTENCY_CONFLICT, RESOURCE_BUSY, INVALID_STATE; REPORT_WINDOW_CLOSED for a report after its deadline, INVALID_KEY_RING for a tenant whose executor key ring will not load, and INVALID_BUNDLE when the active bundle cannot authorize the request in the deployment it is running in. None of the six is the transient dependency fault a 503 names, and they do not clear the same way: INVALID_STATE and REPORT_WINDOW_CLOSED are final for the row that got them, INVALID_KEY_RING and INVALID_BUNDLE stand until an administrator changes something, IDEMPOTENCY_CONFLICT until the caller sends a different idempotency key, and a busy target stops being busy when the live claim on it is released
423TENANT_STOPPED: the tenant's emergency stop is active, so nothing new is admitted, decided or dispatched until an administrator resumes it; a retry of a pending approval answers 202 with its unchanged view plus tenant_stopped: true; human pages show a banner, hide the decide form and answer 423 to a submitted decision
429Gateway/runtime/session capacity reached, or RATE_LIMITED for a credential, sign-in or peer window (with Retry-After)
503Unsafe/unavailable state, policy, database, connector or other dependency; also TOO_MANY_GRANTS, which refuses a ninth outstanding checkpoint grant for one principal and clears as its grants settle

Every response carries X-Request-Id: the request's header when it was a UUID, otherwise a fresh identifier, which the structured logs use to correlate records. Execution-domain errors use {"detail":"Execution unavailable","code":"..."}. Authentication/header/session errors may have only detail. Callers should use status and documented code when present, not parse message text. A transport failure or 503 after possible commitment is not proof of non-execution; use scoped status and administrator reconciliation.

Client package

clients/python/ is the installable client (execbound-client, import execbound_client, Python 3.11 or later, httpx only) with synchronous and asynchronous clients, action_request, intent_key and the tool functions. It encodes the six caller behaviors from the examples plus one more: an approval that expires mid-poll is returned as the stale intent rather than raised. Not published to PyPI; see its README.

Examples

Runnable examples of a caller that handles idempotency keys, pending approvals, the same-request retry, uncertain outcomes and rate refusals correctly, over plain HTTP and over MCP with the official SDK, plus a framework-neutral tool function, live under examples/. They run against the local demonstration and are exercised by tests/test_examples.py; they are not named host compatibility trials.

MCP

Connect using Streamable HTTP at /mcp/, with the same bearer credential and a compatible MCP client. The server is stateless, uses JSON responses and exposes exactly two tools:

ToolArgumentsResult
execute{"request": <ActionRequest>}Same execution view as HTTP, including a checkpoint object for an externally executed operation. The tool description tells the model to pass reference (the ticket, case or run id) and to re-send the identical request to progress the same action, never a new reference or key; it also states that a pending approval is not execution and that a checkpoint grant is neither an execution nor an approval. Every call is logged as mcp.call with the tool, the protocol revision and the client's name and version, identifiers only
execution_status{"execution_id":"<UUID>","continuation":"<optional token>"}Same scoped status view

There is no MCP tool for the outcome report; an executor reports over HTTP.

The execute tool schema is generated from ActionRequest, so it describes the wire shape of arguments as an object of bounded scalars. It deliberately does not disclose per-action argument names or domains; the action catalog documents those. Tool results provide JSON text content and structuredContent; tool errors set isError and contain a bounded error payload. A denied or pending execution is a valid execution result, not necessarily a protocol error. The raw authenticated envelope is capped at 24 KiB; the nested action still follows its 16-KiB contract. Unknown/duplicate envelope fields and conflicting protocol security input are rejected before dispatch.

The pinned server/client is MCP 2.2.0, with the maintained 1.30.0 client installed separately from tests/mcp-legacy.txt for compatibility tests. Exact observed protocol versions and evidence live in Surfaces and the validation manifest. This does not establish compatibility with every named MCP host, OAuth discovery or federated login.

For a running fresh manual fixture, this helper selects private credentials without printing them:

uv run python scripts/validation_request.py --private local-data/manual-demo-1 --target workstation-25 --action endpoint.isolate --key review-one --protocol http
uv run python scripts/validation_request.py --private local-data/manual-demo-1 --target workstation-25 --action endpoint.isolate --key review-one --protocol mcp

Default selection is tenant 0 / agent-0; optional --tenant 1 and --agent agent-1 select other seeded actors. See Validation for creating that fixture and reviewing its pending request.

Human routes

These are server-rendered form workflows, not agent tools or a general JSON administration API. Use the rendered forms so CSRF and signed state correspond to the current session and frozen execution. A failed request that accepts text/html gets an HTML error page carrying the same status code and generic copy; other clients get the JSON error.

Method and pathPurpose
GET /loginCredential-entry page with login CSRF state
POST /sessionEstablish bounded HUMAN_APPROVER or ADMIN session
POST /session/logoutRevoke the current session; the console's Sign out submits this form, as every page did
GET /approvals/{approval_id}/classicThe server-rendered approval page: the frozen plan, the facts with their sources, the obligations, the applicable limits, the decisions so far and the decision form. Every approval notification links it from its footer and the console's own approval route links it from its error boundary, so a phone whose console does not load, or that blocks scripts, still decides (#333, decision 8)
POST /approvals/{approval_id}/decideAssigned HUMAN_APPROVER submits csrf, state and decision; the same route the classic page's form posts to
GET /onboardPublic claim form for an operator-minted sandbox invitation code and a tenant name
POST /onboardRedeems the code through the onboarding_claim definer function and renders the new administrator credential once
GET /showcaseThe public board of one configured tenant: no session, no cookie, Cache-Control: public, max-age=5, registered only when showcase_tenant_id is set
GET /assets/app.cssPackaged stylesheet for the pages above, on the console's tokens and type
GET /assets/showcase.jsThe showcase's refresh module: it reloads the page while the tab is visible and does nothing else; the page works without it
GET /console/{file}The console build's own files: the hashed ones immutable, the rest revalidating by ETag
GET /favicon.ico, GET /favicon.svgThe mark, where a browser asks for it whatever a document says; the console build's own bytes, revalidating by ETag

Every other address a person opens is the console, which serves its own document at /, /activity, /approvals, /approvals/{id}, /agents, /agents/{id}, /approvers, /ask, /ask/{id}, /board, /credentials, /monitor, /monitor/agents/{id}, /people, /people/{id}, /policy, /policy/{id}, /policy/{id}/rules, /settings, /tenant and /welcome, and reads /api/console/v1/ (surfaces). The addresses the console replaced redirect permanently: /admin/people, /admin/people/{id}, /admin/credentials, /admin/approvers, /admin/settings, /policy/rules, /monitor/{view}, the two welcome downloads, and /next with everything under it.

Lists show the latest 50 scoped executions. Decision and reconciliation forms redirect with 303 on success. Sessions expire after 15 idle minutes and no later than the credential; a page load after half that lifetime renews the session (never past eight hours from sign-in or the credential's expiry) and re-issues the cookies, and at most five are active per credential. The decision form stays valid for the session and the approval, not for a fixed two minutes. Cookie digests and CSRF digests are retained server-side. Exact origin, fresh persisted authority and signed form bindings are rechecked around sensitive work. HTTPS origins use Secure __Host- cookies and HSTS. See Surfaces before changing page behavior.

Protected provider protocol

These endpoints belong to separate mock processes and are inaccessible with an agent's gateway token. All listed operations are POST with strict JSON:

ProviderPathCredential / action
Endpoint/v1/containAccount dispatch credential / endpoint.isolate
Endpoint/v1/lift_containmentAccount dispatch credential / endpoint.lift_isolation
Identity/v1/disableAccount dispatch credential / identity.disable
Identity/v1/enableAccount dispatch credential / identity.enable
Identity/v1/revoke_sessionsAccount dispatch credential / identity.revoke_sessions
Identity/v1/reset_passwordAccount dispatch credential / identity.reset_password
Both/v1/journalMatching dispatch or control credential / exact operation evidence lookup
Both/v1/cancelControl credential only / atomic no-effect tombstone if possible

Each service registers one dispatch path per catalog binding of its configured provider. Commands freeze operation/account/tenant/resource identity, expected version, action, the action's normalized arguments and intent/plan hashes. The strict schemas in provider/models.py are the wire authority. Terminal evidence must bind the complete command and valid HMAC, with operation-specific state transitions verified before settlement. There is no public provisioning, failure-injection, reset or arbitrary passthrough route. Details: Provider, Identity provider.

CLI and operator scripts

All commands run from the repository root with the locked environment. Use --help for the exact argument parser. Tokens belong in private files, not command-line arguments.

CommandArguments / boundary
uv run execbound seed-demoOptional --output (default local-data/demo.json); local identity-only synthetic seed; owner connection
uv run execbound seed-validationRequired --output, optional --seed, --gateway-port, --endpoint-port, --identity-port; complete local fixture; owner connection
uv run execbound audit-exportRequired --tenant, --credential-file, --output; optional --checkpoint; current ADMIN + restricted DB connection, read-only export
uv run execbound replayRequired --input, --trusted-head, --output; optional --candidate; offline files only, new output directory
uv run execbound tenant-createRequired --name; owner connection; prints the new tenant ID; names are unique case-insensitively
uv run execbound invite-createExactly one of --slots-file (the seeder's slots.json; the lowest unclaimed slot without a live invitation is taken, an expired one being revoked first) or --tenant (one unclaimed slot); required --note, --output; optional --lifetime-hours (default 168, 1 to 720); owner connection; mints a single-use ebi1 invitation code, writes it once to the new owner-only output file and prints identifiers, the slot and the expiry; refuses an empty pool or a slot with a live invitation (INVALID_SLOT, INVITATION_LIVE) and an existing output path
uv run execbound invite-listExactly one of --slots-file or --tenant; owner connection; every invitation of the slot or slots with its note, times, failure_count and state (live, expired, redeemed, revoked); never the code
uv run execbound invite-revokeRequired --tenant, --invitation; owner connection; revokes an unredeemed invitation so the slot can be invited again
uv run execbound sandbox-seedRequired --count (1 to 50), --seed (a label unique per batch), --output (a new directory), --gateway-origin, --endpoint-origin, --identity-origin; owner connection, development harness; seeds that many inactive demo-seeded slots in one transaction and writes slots.json, the gateway connectors, the two mock account files, the two mock resource files, the jobs token map and a sandbox-<nn>.json driver file per slot; a repeated label or an existing directory writes nothing
uv run execbound sandbox-refreshEither --tenants-file (as for recover) or both --tenant and --credential-file; current ADMIN; re-attests a claimed sandbox's facts and reconciles its provider versions from its own receipts through sandbox_reattest, printing reattested per tenant; refuses a tenant without a claimed slot (INVALID_SLOT)
uv run execbound sandbox-adoptRequired --tenant, --seed, --slot, --actor; owner connection; records an active tenant as a claimed slot (stamped seeded and claimed now, audited as sandbox.adopted); refuses an inactive tenant, a non-ADMIN actor and a tenant with a slot row
uv run execbound mock-reprovisionRequired --provider endpoint|identity, --tenant, --target; optional --version, --reason. Run inside the mock's own environment (EXECBOUND_MOCK_DATABASE_PATH, and EXECBOUND_MOCK_RESOURCES_JSON when no version is stated), never against the gateway: it opens no gateway database. Resets exactly the one target named. --version sets the version and touches nothing else, which is what a target stranded by version drift needs, because resetting the containment or the disabled account it legitimately holds would contradict the audit chain. Without --version it resets to the resources file's values in full. Refuses a target the mock never held, and logs mock.reprovisioned with the tenant, the target, the old and new version and the reason
uv run execbound tenant-stop / tenant-resumeRequired --tenant, --credential-file, --reason; current ADMIN + restricted connection; activates or releases the tenant emergency stop inside the tenant fence, audited, printing the stop identifier
uv run execbound tenant-statusRequired --tenant, --credential-file; current ADMIN; whether the emergency stop is active, with its identifier, time, administrator and reason, the enforcement mode and the approval deadline (approval_deadline_seconds, null when off)
uv run execbound tenant-modeRequired --tenant, --credential-file, --set ENFORCE|OBSERVE, --reason; current ADMIN; switches the tenant's enforcement mode inside the fence, audited as tenant.mode_changed; a no-op is refused
uv run execbound tenant-approval-deadlineRequired --tenant, --credential-file, --set <seconds|off>, --reason; current ADMIN; sets how long a new approval may wait, 120 to 3600 seconds or off (the frozen plan's validity alone), inside the fence, audited as tenant.approval_deadline_changed; anything else is INVALID_DEADLINE, the current value INVALID_STATE; pending approvals keep their expiry
uv run execbound observe-reportRequired --tenant, --credential-file; optional --since-hours (default 24); current ADMIN; counts observed admission decisions from the audit chain by effect, action and reason and lists every limit-driven outcome that relied on the assumed-executed projection
uv run execbound rotation-readinessRequired --tenant, --credential-file; current ADMIN; unsettled operations per account and the latest approval and session expiries that still depend on the current keys
uv run execbound reconcileRequired --tenant, --credential-file, --execution, --reason; optional --cancel-if-missing; current ADMIN with the private configuration's connectors; settles one committed or indeterminate execution from the provider's journal through the same path as the console form and prints the resulting view. Not the administrator exit for a checkpoint grant: it looks the account up in the live connector inventory and refuses, and the connector path refuses a grant in every mode. Use the Activity page's settlement control
uv run execbound recoverEither --tenants-file (a JSON object of tenant id to jobs token, at most 64; one run per tenant, a refused tenant reported as {"skipped": code} and the exit code 0) or both --tenant and --credential-file; optional --limit (1 to 50), --grace-seconds (60 to 86400), --cancel-unattempted; current ADMIN with the private configuration's connectors; one bounded recovery run through the reconciliation path, printing counts and the run identifier. It also sweeps unreported checkpoint grants into retained uncertainty without settling, refunding or releasing anything, and releases the canonical claim a settlement on an executor's bare word left held once the act window has passed
uv run execbound notifyRequired --tenant, --credential-file; optional --limit (1 to 50); current ADMIN with the private configuration's destinations; delivers pending approval notifications with lease, retry and backoff, printing counts
uv run execbound notificationsRequired --tenant, --credential-file; optional --state; current ADMIN; lists approval notifications with delivery state, attempts and error codes
uv run execbound export-events / export-status / export-resumeRequired --tenant, --credential-file; export-events takes --limit (1 to 1000) and export-resume takes --destination; current ADMIN with the private configuration's security-events destinations; ordered, at-least-once export of the sanitized audit chain and failure summaries through per-destination cursors
uv run execbound principal-createRequired --tenant, --kind; --actor (an ADMIN principal) required except for a tenant's first ADMIN; owner connection, audited
uv run execbound credential-issueRequired --tenant, --principal, --actor, --output; optional --lifetime-hours (default 8, maximum 2160); writes the token once to a new exclusively created file, owner-only where the platform carries POSIX modes (Operations), and prints only identifiers; owner connection, audited
uv run execbound credential-listRequired --tenant; owner connection; identifiers, kinds, timestamps and each credential's authentication failures in the last 24 hours, never digests
uv run execbound credential-revokeRequired --tenant, --credential, --actor; owner connection, audited
uv run execbound approver-assign / approver-unassignRequired --tenant, --approver, --target, --action, --actor; --action accepts every action in the action catalog, so a new action is assignable as soon as it is released; the approver must be a HUMAN_APPROVER and the target a known canonical target; owner connection, audited
uv run execbound group-createRequired --tenant, --name (1 to 80 characters after trimming, unique within the tenant), --actor; owner connection, audited as group.created; prints the new group ID
uv run execbound group-assign / group-unassignRequired --tenant, --group, --principal, --actor; the principal must be an active AGENT (INVALID_PRINCIPAL) and the group must exist (INVALID_GROUP); a repeated, missing or 65th membership is INVALID_MEMBERSHIP; both take the tenant fence so the change is ordered against every decision; owner connection, audited as group.assigned / group.revoked
uv run execbound group-listRequired --tenant; owner connection; each group's identifier, name, creation time and sorted member identifiers
uv run execbound executor-key-registerRequired --tenant, --key-id, --public-key (an executor's Ed25519 public key as 64 lowercase hex characters; uppercase is normalized), --actor; owner connection, audited as executor_key.registered; refuses a taken id, material already live for the tenant, material already recorded compromised and any encoding nobody holds the private half of
uv run execbound executor-key-rotateRequired --tenant, --key-id, --new-key-id, --public-key, --actor; registers the replacement and retires the old row as RETIRED in one transaction, so a proof for a grant issued while the old key was current still verifies; audited as executor_key.rotated
uv run execbound executor-key-revokeRequired --tenant, --key-id, --actor; retires one live key as RETIRED; refused while it is the tenant's last live key and an active bundle declares an external executor; audited as executor_key.revoked
uv run execbound executor-key-compromiseRequired --tenant, --key-id, --actor; records COMPROMISED, which the verifier reads retroactively, over a live or already retired row, for every id of the tenant carrying that public key; it may leave the ring empty, and admission then refuses new grants for the tenant until a replacement is registered; audited as executor_key.compromised. See Operations for what it costs
uv run execbound executor-key-listRequired --tenant; owner connection; each key's id, registered public key, creation and revocation times and revocation reason; writes and audits nothing
uv run execbound account-createRequired --tenant, --provider (a provider the action catalog binds), --actor; owner connection, audited as account.created; prints the new account ID
uv run execbound mapping-createRequired --tenant, --account, --action (catalog action bound for the account's provider), --actor; optional --ref (operation ref, default the action name, unique per tenant) and --mapping-version (default map-v1); provider operation, schema version and contract come from the catalog; audited as mapping.created; prints the mapping
uv run execbound target-createRequired --tenant, --actor; owner connection, audited as target.created; prints the canonical target ID
uv run execbound resource-createRequired --tenant, --account, --resource-type, --provider-id, --source (an active inventory context source of the account), --actor; optional --target (existing canonical target, else a new one), --hostname (endpoints only), --version (default resource-v1), --alias-days (default 365), --role, --privilege-class, --environment, --criticality (checked against the contract domains); creates the resource, its identity and its provider_id and hostname aliases; audited as resource.created; prints the resource, target and aliases
uv run execbound account-list / mapping-list / resource-listRequired --tenant; owner connection; accounts with provider and state, mappings with their catalog-derived fields, resources with target and unexpired aliases
uv run execbound failures--tenant or --unattributed; optional --since-hours (default 24, maximum 2160); owner connection; failed-authentication windows by surface, reason, minute and verified subject, never tokens or addresses
uv run execbound ruleset-compileRequired --tenant, --input, --output; owner connection, reads the tenant's active mappings, writes the bundle document the rules compile to and nothing else; refuses to overwrite, refuses a tenant with no mapping, reports a bad rule at its field location
uv run execbound bundle-createRequired --tenant, --credential-file, --input; JSON bundle file (tenant_id and bundle_id may be omitted); current ADMIN + restricted connection, audited DRAFT
uv run execbound bundle-validate / bundle-activate / bundle-retireRequired --tenant, --credential-file, --bundle; current ADMIN + restricted connection; audited lifecycle transitions; a quorum rule naming an unknown obligation is refused with its field location (quorum.<n>.obligation); a limit rule naming an action outside the catalog, an unknown group, an unknown or inactive account, or a selector fact no serving contract declares or a value outside its domain is refused at limits.<n>.action, limits.<n>.group_id, limits.<n>.account_id or limits.<n>.selector
uv run execbound bundle-listRequired --tenant, --credential-file; current ADMIN; lifecycle state and active selection per bundle
uv run execbound bundle-previewRequired --tenant, --credential-file, --bundle; optional --window-hours (default 24, maximum 720); current ADMIN; read-only in-memory replay of the candidate against recent frozen history, listing decisions whose effect would change, each with the candidate's effective approver requirement; a candidate group rule over captures without recorded membership marks the affected decisions UNKNOWN_GROUP_MEMBERSHIP
uv run python scripts/local_setup.pyOptional --postgres-bin; prepare local DB/roles/private environment, no migration
uv run python scripts/validation_demo.py serveRequired --private; foreground manual gateway/providers
uv run python scripts/validation_demo.py runRequired --private and --output; automatic browser/provider/replay demonstration
uv run python scripts/demo_driver.pyRequired --private (directory holding sandbox.json and the credential files) and a beat (boundary, decide, budget, replay or all); optional --run, --wait, --poll; the six recorded-demo outcomes against a sandbox tenant over HTTP and MCP, the audit export and offline replays; writes demo-record.json beside the inputs; replay needs DATABASE_URL
uv run python scripts/validation_request.pyRequired --private, --target, --action, --key; optional --tenant, --agent, --protocol
uv run python scripts/validation_evidence.pyRequired --junit, --collected, --evidence, --output; validate and hash the evidence allowlist
uv run python scripts/admission_benchmark.pyRequired --private, --output; optional --seconds, --concurrency, --tenants; denied-admission throughput and latency per concurrency level against fresh fixtures
uv run alembic upgrade headSeparate owner migration connection; explicitly apply reviewed migrations

Bundle create/validate/activate/retire/list functions live in policy/store.py and back the bundle-* commands; tenant, principal, credential, approver-assignment and principal-group operations live in administration.py and back the owner-connection commands; the administration pages (surfaces/admin_pages.py) make the same principal, credential and assignment changes through the SECURITY DEFINER functions of migration 0021_admin_functions, which refuse administrator principals and credentials. Bundles may carry context_settings that tune a context contract's max age and permitted writers; see Context contracts. Limit rules may be scoped to a principal group, a connector account or a selector over a trusted fact and may span several actions; see Policy. Every administrative change appends an audit event under the acting ADMIN principal; a tenant's first ADMIN is audited as its own actor. Bundle management is also available on the policy pages. No inventory sync service or HTTP interface provisions trusted context: resources columns, incidents and resource_facts rows are written only through the owner connection, by context-import from adapter-produced manifests, and context sources by context-source-create and context-source-deactivate (provisioning.py; see Context contracts). Do not infer other interfaces from design proposals.