{"protocol":"clank-doc/1","frameworkVersion":"0.24.0","slug":"deployment-dependencies","title":"Deployment dependency gates","description":"Require managed services before activating new code on a target project. Gates apply to ordinary uploads, local canaries, explicit rollback, environment promotion and channel activation. Configure Project → Environments → Required services ","group":{"id":"deploy","title":"Deploy and operate"},"url":"https://docs.clank.run/docs/deployment-dependencies","source":"docs/deployment-dependencies.md","headings":["Configure services","Review exact services","Rollback and exact retries","Human readiness approval","Provider scope","Interruption and recovery","HTTP contract"],"tableOfContents":[{"id":"configure-services","title":"Configure services","level":2},{"id":"review-exact-services","title":"Review exact services","level":2},{"id":"rollback-and-exact-retries","title":"Rollback and exact retries","level":2},{"id":"human-readiness-approval","title":"Human readiness approval","level":2},{"id":"provider-scope","title":"Provider scope","level":2},{"id":"interruption-and-recovery","title":"Interruption and recovery","level":2},{"id":"http-contract","title":"HTTP contract","level":2}],"markdown":"# Deployment dependency gates\n\nRequire managed services before activating new code on a target project. Gates apply to ordinary\nuploads, local canaries, explicit rollback, environment promotion and channel activation.\nConfigure **Project → Environments → Required services for this project**, or use the linked CLI.\nConfigure each target independently; a family root's requirements do not implicitly configure\nits staging or production projects. Accepted writer restart is independent of new readiness.\n\n## Configure services\n\nA requirement names a project in the same workspace, readiness `active` or `healthy`, and an\noptional exact upload SHA-256 `digest`. Individual projects must have the same owner. Preview\nprojects, self references, cycles, duplicates, external URLs, custom headers and scripts are\nrejected. At most 16 services are required. Every check and activation requires current target\nauthority and current read access to every service.\n\n```sh\nclank dependency get --json\n```\n\nAn unconfigured target returns version zero, no requirements, a 5,000 ms deadline and denied\noverrides. Save a bounded JSON file, for example `dependencies.json`:\n\n```json\n{\n  \"requirements\": [\n    { \"projectId\": \"MANAGED_SERVICE_ID\", \"readiness\": \"healthy\" }\n  ],\n  \"timeoutMs\": 5000,\n  \"overridePolicy\": \"deny\"\n}\n```\n\n```sh\nclank dependency configure --config=dependencies.json --expected-version=0 --json\nclank dependency get --json\nclank dependency check --expected-version=1 --json\nclank dependency history --json\nclank dependency activations --json\n```\n\nConfiguration requires a current owner/admin, target token-management permission and the\ninstallation's configured fresh-authentication policy. Initial setup requires an idle project\nwithout a deployment lease or staged writer. Later updates increment the version and invalidate\nactivations that captured the old configuration. Concurrent saves admit one version; stale\nsaves return `DEPENDENCY_VERSION_STALE`. Inspect current configuration after a lost save response\nbefore reviewing another update. An empty list preserves the versioned configuration.\n\nRequired services cannot be deleted until references are removed. Configuration publication\nand deletion share a graph lease before any service files are removed.\n\nThe total health deadline is 100–10,000 ms. Up to four probes run concurrently, with a maximum\n1,500 ms per request. `active` requires a successfully active release; `healthy` also requires\nits exact managed runtime to answer its configured health path with a successful HTTP status.\nAn optional digest must match the retained upload. Redirects are not followed. Response bodies,\napplication errors and runtime credentials are not retained. Fixed reasons distinguish inactive\ncode, digest mismatch, unavailable runtime, failed health and timeout.\n\n## Review exact services\n\nA check returns its ID, configuration version and exact release, upload digest, activation time,\nprovider generation when applicable, and monotonic activation sequence. The private review also\nbinds workspace, placement and node. History retains the newest 100 checks and newest 100\ncompleted activation receipts, plus all unresolved work. Current permission filters or redacts\ndependency details.\n\nExplicit checks last five minutes and belong to their creating credential. A browser session's\ncheck cannot be used by a device-token CLI request, even for the same account. Activation probes\nhealth again while requiring the reviewed configuration and service identities to remain exact.\nReplacing a service with an identical upload still changes its activation sequence. Expired or\npruned unaccepted checks fail with `DEPENDENCY_CHECK_EXPIRED`; inspect state, check again and\nuse a new activation key.\n\n```sh\nclank dependency check --expected-version=1 --json\nclank deploy --dependency-version=1 --dependency-check=CHECK_ID --json\n```\n\nEnvironment and channel activation accept the same optional flags alongside their existing\nexact artifact, target, versions and request key. Create the check with the same CLI credential\non the target project. Dashboard promotion/channel review checks the target automatically,\ndisplays the exact services, and retains the check ID and version with the reviewed request.\nEditing fields invalidates review. Pending exact requests survive project navigation in that\nbrowser session; reload requires review again. Sign-out and revoked project access clear drafts\nand visible details.\n\nConfigured clients without an explicit check capture the current services during activation.\nUnconfigured existing clients retain their original behavior. An explicit version or check\nopts even an empty configuration into reviewed activation. Gates protect new code acceptance;\nthey do not continuously stop accepted writers when a service later becomes unhealthy. Accepted\nwriter restart and verified prior-writer compensation bypass new readiness. Current identity\nand authority are checked across awaits and in the acceptance publication transaction.\n\n## Rollback and exact retries\n\nConfigured rollback requires an inactive retained release, an explicit request key, and the\ncurrent target release and monotonic activation sequence from `dependency get`. The sequence\nprevents an old request becoming valid just because the target returns to the same release.\n\n```sh\nclank dependency get --json\nclank dependency check --expected-version=1 --json\nclank rollback OLD_RELEASE_ID --key=reviewed_rollback_0001 \\\n  --expected-active=CURRENT_RELEASE_ID --expected-activation=ACTIVATION_SEQUENCE \\\n  --dependency-version=1 --dependency-check=CHECK_ID --json\n```\n\nKeep all fields and the key unchanged on retry. Accepted replay returns its original result\nwithout activating old code again or checking current service health. Current permission still\napplies. Changed input fails with `DEPENDENCY_RETRY_CHANGED`. Expired ordinary upload receipts\nfail closed rather than silently accepting their keys as new operations. Failed work requires\ninspection and a new request after recovery; a new key cannot bypass an unresolved writer.\nReviewed rollback to an already active release is rejected instead of retaining an ambiguous\nno-op request.\n\nLocal rollback supports the existing `--restore-data --confirm=\"restore TARGET_SLUG\"` contract.\nIt retains a separate durable safety snapshot before copying older data, so a failed or\ninterrupted rollback restores its own prior writer and current data.\n\n## Human readiness approval\n\nOverrides default to denied. `overridePolicy: \"administrator\"` permits an explicit human browser\nowner/admin with current token-management permission and configured fresh authentication.\nDevice and project tokens cannot approve readiness overrides. The dashboard requires an 8–500\ncharacter reason and exact typed confirmation:\n\n```text\noverride-dependencies TARGET_SLUG CONFIGURATION_VERSION\n```\n\nApproval binds the configuration, credential, exact service identities and activation request,\nand is audited on acceptance. It bypasses readiness alone. Changed identity, revoked authority,\ncertification, attestation, migration policy, target changes and recovery fences still reject.\nThe CLI provides no human-approval substitute.\n\n## Provider scope\n\nGated provider activation requires a current operator certificate for the exact assigned,\nco-located Docker/XFS host. Initialized targets verify it before staging and before acceptance.\nInitial targets verify their newly assigned host before acceptance. Remote hosts need matching\nremote proof; a local certificate does not certify Railway or another production machine.\n\nAfter initialization, gated provider uploads and rollback preserve the exact migration corpus\nand database path. New, removed or changed migrations are rejected before staging. Provider\nrollback activates code only; use the separately reviewed provider data-recovery workflow for\ndata restoration. Private compensation restores the prior generation's frozen environment so\nnewly edited candidate secrets cannot break prior accepted code.\n\nA failed first activation verifies that its exact owned generation stopped and retains its\ninitialized application data. When the provider actually observed that generation running,\nrecovery records its initialization, host and exact artifact durably. The artifact remains\nprotected until a subsequent reviewed activation accepts a writer. That activation preserves\nthe initialized data and exact migration corpus, including after controller restart. A failure\nwithout a verified running generation does not establish initialization proof; ambiguous data\nrequires operator inspection. Missing, changed or expired host proof keeps recovery fenced until\nthe operator verifies the host again.\n\n## Interruption and recovery\n\nA durable receipt captures dependencies, prior target, candidate and the data-change boundary\nbefore destructive work. Controller interruption fences new writers and ingress until verified\nrecovery. Neither an exact staged local retry nor a new key can claim that the candidate committed.\n\n```sh\nclank dependency activations --json\nclank dependency recover ACTIVATION_ID \\\n  --confirm=\"recover-dependencies TARGET_SLUG ACTIVATION_ID\" --json\n```\n\nRecovery requires current rollback permission, owner/admin role, configured fresh authentication\nand exact confirmation. The dashboard lists interrupted work under **Dependency checks and\nactivations**. Recovery verifies owned artifacts, snapshot identity/size, current target and\nprovider host, then stops the candidate and restores the prior writer. It may compensate after\nservice access is lost without granting that access again. Unverified cleanup remains\n`recovery-required`; prior/candidate artifacts remain protected from deletion.\n\nFor environment or channel activation, recover through the original environment promotion\nhistory. That finishes both durable receipts together. Standalone dependency recovery rejects\nunresolved combined work with `PROMOTION_RECOVERY_REQUIRED`.\n\nBefore downgrading, recover every unresolved activation, clear each requirement list and verify\nthere are no dependency edges. Retained provider initialization must have a verified accepted\nwriter or its project must be removed through the explicit project deletion workflow.\nAdditive tables may remain, but older controllers cannot enforce\ngates or deletion protection. Do not run old and new controllers concurrently on one control store.\n\n## HTTP contract\n\nRoutes require current target permission and existing CSRF/session or bearer-token contracts.\nBounded bodies reject unknown fields.\n\n| Method | Route | Contract |\n| --- | --- | --- |\n| GET | `/api/projects/:id/dependencies` | Configuration plus target release and activation sequence |\n| PUT | `/api/projects/:id/dependencies` | `expectedVersion`, `requirements`, `timeoutMs`, `overridePolicy` |\n| POST | `/api/projects/:id/dependencies/check` | Exact `expectedVersion`; credential-bound retained check |\n| GET | `/api/projects/:id/dependencies/checks` | Newest 100 currently visible reports |\n| GET | `/api/projects/:id/dependencies/activations` | Retained state with authorized or redacted details |\n| POST | `/api/projects/:id/dependencies/activations/:activation/recover` | Exact `confirmation`; verified recovery |\n\nUploads accept optional `x-clank-dependency-version`, `x-clank-dependency-check`, and a bounded\nJSON `x-clank-dependency-override` header. Promotion, channel activation and rollback bodies\naccept optional `expectedDependencyVersion`, `dependencyCheckId` and `dependencyOverride`.\nOverride contains `expectedVersion`, `reason` and `confirmation`. Configured explicit rollback\nalso requires `idempotencyKey`, `expectedActiveReleaseId` and `expectedActivationSequence`.\nExisting expected target, environment/channel versions, attestation and idempotency fields\nremain part of the exact request.\n\nUse [scheduled release windows](release-windows.md) to review an exact current channel pin for\na bounded future window. Execution rechecks current authority and required services, cancellation\nprevents acceptance, and interrupted work retains verified recovery fences. The dashboard exposes\n**Schedule current pin** and **Scheduled releases** in the Environments tab.\n"}