> Call POST /v1/check before a tool that writes. DENY means do not invoke the tool. For clean Markdown of any page, append .md to the page URL. For the complete index, fetch https://docs.withwrit.com/llms.txt. # Enforcement # Enforcement Enforcement adds three gate capabilities for tenant agents and CI pipelines: 1. **Credential vault** — envelope-encrypted API keys (Stripe only in Phase 1). 2. **Gated execution** — single-use leases that call a fixed upstream host with a vaulted credential. 3. **CI attestation** — signed `writ scan` manifests and per-repo drift. These endpoints are tenant-facing and documented here. Portal-only endpoints (vault enrollment and revocation, which require a signed-in session + TOTP) are internal and omitted. --- ## Credential vault The vault stores one credential per enrolled system per tenant. Phase 1 supports Stripe only (`systemId: stripe`). * Credentials are **envelope-encrypted** with AWS KMS `GenerateDataKey` and an encryption context `{tenant_id, system_id}`. * The plaintext credential exists only in gate memory at execute time. * After enrollment the API key is **never displayed or returned** again. * Revocation sets the credential status to `revoked` and burns outstanding leases. Enroll and revoke are portal-only actions: sign in at [https://app.withwrit.com](https://app.withwrit.com), go to **Enforcement**, and confirm with your authenticator app. --- ## Gated execution ### 1. Get an ALLOW token Your agent calls `POST /v1/check` as usual. An ALLOW response contains an `authToken` bound to the exact write. ```bash curl -X POST https://api.withwrit.com/v1/check \ -H "Authorization: Bearer $WRIT_KEY" \ -H "Content-Type: application/json" \ -d '{ "sponsorId": "acme", "agentId": "refunds-agent", "verb": "refund", "target": "order-1001", "purpose": "refund duplicate charge" }' ``` ### 2. Create a lease Convert the ALLOW token into a single-use lease bound to the exact Stripe request you intend to send. The lease TTL is capped to the token's remaining life (≤ 90 seconds). ```bash curl -X POST https://api.withwrit.com/v1/leases \ -H "Authorization: Bearer $WRIT_KEY" \ -H "Content-Type: application/json" \ -d '{ "token": "$AUTH_TOKEN", "method": "POST", "path": "/v1/refunds", "body": {"charge": "ch_3N..."} }' ``` ### 3. Execute Call `POST /v1/execute` with the lease id and the **same** method/path/body. ```bash curl -X POST https://api.withwrit.com/v1/execute \ -H "Authorization: Bearer $WRIT_KEY" \ -H "Content-Type: application/json" \ -d '{ "leaseId": "$LEASE_ID", "systemId": "stripe", "method": "POST", "path": "/v1/refunds", "body": {"charge": "ch_3N..."}, "idempotencyKey": "refund-order-1001" }' ``` The gate returns Stripe's status and body verbatim. If the lease is expired, reused, the request bytes don't match, or the credential is revoked, the gate fails closed: no upstream call, and an error receipt. ### Constraints * `systemId` must be `stripe` in Phase 1. * `path` must begin with `/v1/`. * `body` must be JSON and ≤ 1 MB. * No streaming, no multipart, no arbitrary URLs. --- ## CI attestation `writ scan` can emit a signed manifest for CI. ### Fetch the manifest key Issue once per tenant: ```bash writ manifest-key --key $WRIT_KEY ``` Store the returned key in CI as `WRIT_MANIFEST_KEY`. ### Produce a signed manifest ```bash writ scan --manifest-out manifest.json --key $WRIT_KEY ``` The manifest includes: * `repoId` * `commitSha` * `scannerVersion` * per-language findings summary * ungated write-sites (`path` + `verb`) * `policyFileHash` * `timestamp` It is signed with HMAC-SHA256 over the canonical JSON. ### Submit the manifest ```bash curl -X POST https://api.withwrit.com/v1/manifests \ -H "Authorization: Bearer $WRIT_KEY" \ -H "Content-Type: application/json" \ -d @manifest-payload.json ``` where `manifest-payload.json` is: ```json { "repoId": "myrepo", "manifest": { ... }, "signature": "..." } ``` The gate verifies the signature, stores the manifest, and returns drift vs the previous manifest: * `newUngatedSites` * `resolvedSites` * `policyHashChanged` * `scannerVersionChanged` View the latest manifest and drift in the portal under **Enforcement**, or via `GET /v1/manifests` and `GET /v1/manifests/{repoId}`. --- ## API reference See the **Enforcement** endpoints in the API reference: * `POST /v1/leases` * `POST /v1/execute` * `GET /v1/manifest-key` * `POST /v1/manifests` * `GET /v1/manifests` * `GET /v1/manifests/{repoId}`