Deployment platform
Clank Deploy is the open source control plane for turning a Clank source directory into a running release. It uses Fetch, Node, SQLite, Web Crypto, and no runtime NPM dependencies.
Clank Deploy is the open-source control plane for turning a Clank source directory into a running release. It uses Fetch, Node, SQLite, Web Crypto, and no runtime NPM dependencies.
The platform is intentionally inspectable:
- deployment configuration is checked-in JSON;
- builds run locally as an argument array, never as a server-side shell hook;
- every artifact and file has a SHA-256 digest;
- artifacts contain the exact Clank runtime used by the CLI;
- SQL migrations have ordered, immutable checksums;
- releases, failures, rollbacks, backups, job cancellation/retry, tokens, secret-name changes, and projects are audited;
- secret values are encrypted and never returned by the API;
- failed migrations or health checks restore the prior database and process.
The browser console is also an operating surface, not only a login page. It shows site health, 1-hour through 30-day ingress performance, enforced capacity, releases, private queue health and cron schedules, scheduled encrypted backups, logs, and the full custom-domain lifecycle. See Deployment dashboard, quotas, and domains.
Five-minute managed path
clank login
clank create my-todo
cd my-todo
npm install
clank doctor
clank deployclank login connects to https://clank.run by default. The first deploy creates and links an isolated project automatically. Pass --name, --slug, or --org on that command when the directory name is not the intended platform identity.
Local control-plane development
Run the open-source platform locally only when developing or self-hosting the control plane:
CLANK_PLATFORM_DATA=.clank-platform \
CLANK_PLATFORM_URL=http://127.0.0.1:4200 \
npm run dev:platform
clank login --server=http://127.0.0.1:4200Create the bootstrap account at http://127.0.0.1:4200 before approving the local device login. Remote platform URLs must use HTTPS; loopback HTTP is accepted only for development.
Device login
clank login follows the interaction in RFC 8628:
- The CLI requests a high-entropy device code and short user code.
- The user signs in at the exact platform origin and reviews the client name and code.
- Approval requires the browser session's CSRF token.
- The CLI polls at the server-provided interval.
- The raw access token is returned exactly once. Only its SHA-256 hash is stored by the platform.
- The CLI stores it in
~/.clank/config.jsonwith mode0600.
Device codes expire after ten minutes by default. Access tokens expire after 90 days by default. clank logout revokes the current token before deleting it locally.
Self-registration defaults to bootstrap: only the first account can register. Operators may explicitly choose public or disabled registration.
Owners and administrators invite everyone else from Workspace → People. Configure a mail driver to turn invitation creation into automatic email delivery; without one, Clank visibly keeps the copy-once token workflow. Pending mail is durable across restarts, leased across control-plane instances, and encrypted only until it is sent or invalidated. Read Invitations and email delivery for setup and guarantees.
Version 0.7.0 preserves existing Proact accounts, sessions, platform records, and CLI links while moving all writes to Clank names. Review Renaming from Proact before upgrading a hosted control plane.
Deployment configuration
Every app has clank.deploy.json:
{
"version": 1,
"entry": "dist/server.js",
"include": ["dist", "public", "migrations"],
"build": {
"command": ["clank", "build", "src", "dist"]
},
"database": {
"path": "app.sqlite",
"migrations": "migrations",
"allowUnsafeMigrations": false
},
"health": {
"path": "/healthz",
"timeoutMs": 15000
},
"env": {
"FEATURE_SET": "stable"
},
"jobs": {
"entry": "dist/jobs.js",
"workers": 2,
"concurrency": 4,
"queues": [],
"scheduler": true
}
}Rules:
entrymust be compiled.jsor.mjsinside an included path.- Include paths are literal files or directories, not shell globs.
- Symbolic links, special files, parent traversal,
.env*, private-key names, and VCS metadata are rejected. build.commandis executed locally without a shell.envis public artifact configuration; credentials belong in platform secrets.PORT,HOST,NODE_OPTIONS, andCLANK_*variables are reserved, except the bounded application capacity settingsCLANK_AUTH_CONCURRENCY(1–16),CLANK_AUTH_MAX_QUEUE(1–128), andCLANK_MAX_LIVE_CONNECTIONS(1–20,000). See capacity sizing.database.pathis persistent project data outside release directories.- Changing
database.pathduring deployment is rejected to prevent silently forking production data. database.previewData, when present, is the active production release's bounded sanitization contract for explicitclank preview deploy --data=sanitizedbranches. It is never read from the preview artifact, and raw database copying remains unavailable.jobs.entryis one compiled provider-neutral worker/scheduler module inside an included path.jobs.workerscontrols independent processes;jobs.concurrencycontrols handlers per worker.jobs.queuesis an optional allowlist andjobs.schedulerenables one independently leased cron scheduler.- Provider placement reserves one runner process slot for the web process, one for each worker, and one for the scheduler. A release waits without partial activation when no matching node has the complete slot demand.
Artifact protocol
The wire media type is application/vnd.clank.deploy+gzip. Its document protocol is clank-deploy/1 and contains:
- normalized configuration;
- builder protocol, Clank version, Node version, and source revision;
- SHA-256 bindings for normalized configuration and the complete material manifest;
- the exact ordered immutable migration IDs;
- a sorted file list with path, size, mode, SHA-256 digest, and base64 content.
The gzip timestamp is fixed, so identical inputs on the same Clank and Node versions produce identical bytes. The CLI also sends an artifact digest and idempotency key.
clank deploy --dry-run
clank inspect .clank/artifacts/<digest>.clank.gzDry-run artifact creation is offline and does not require platform credentials. Ambiguous upload failures retain a private local attempt record for 24 hours, allowing the next identical command to reuse its idempotency key instead of accidentally creating a second release after a lost response.
The decoder verifies the configuration, material, and migration bindings before returning an artifact. Set CLANK_SOURCE_REVISION when a nonstandard CI system cannot provide GITHUB_SHA or RAILWAY_GIT_COMMIT_SHA. The metadata supports the traceability goals of SLSA provenance, but clank-deploy/1 is not a signed SLSA attestation. Signing and transparency-log integration are future extensions.
Release transaction
Deployment runs in this order:
- Authenticate the bearer token and verify project ownership.
- Enforce request, gzip, file-count, file-size, and aggregate limits.
- Verify config, paths, modes, file hashes, and artifact digest.
- Extract into a non-active release directory.
- Verify migration history and create a consistent SQLite backup while the active release continues serving.
- If no migrations are pending, gracefully quiesce prior workers/scheduler while its web process continues serving, then launch the candidate web, workers, and scheduler.
- Poll the candidate's configured health route and verify background-process startup without exposing it to public traffic.
- Atomically switch managed ingress and active-release metadata to the healthy candidate. Resume the prior background set instead if the candidate fails.
- Let requests already assigned to the prior web upstream finish, with a bounded two-second drain for long-lived streams, then stop the prior release.
A code-only candidate that fails startup or health never receives traffic and never stops the prior release. Automatic crash recovery uses the same durable project lock as deploy, rollback, backup, and deletion, so it cannot race a user deployment for the project's runtime ports.
An unexpected worker or scheduler exit crashes and restarts the complete release group instead of leaving a healthy-looking web process with stale background work. Read Durable jobs and cron for queue correctness and process-provider requirements.
Pending SQLite migrations use the safer exclusive path: Clank stops the prior release, applies all pending SQL in one BEGIN IMMEDIATE, starts and checks the candidate, and restores the verified snapshot plus prior release if anything fails. This creates a short maintenance window because arbitrary local schema changes cannot safely run beside unknown old application code. Continuously writable multi-instance systems and zero-downtime schema changes need an external database plus expand/contract migrations.
Before creating a release directory, Clank checks the site's retained-artifact count and the uncompressed bundle plus current SQLite/WAL footprint under the durable project lock. After taking a pre-deploy snapshot, it records the exact snapshot size. This bounds cumulative deployment storage rather than only bounding each upload.
clank releases
clank releases delete <inactive-release-id> \
--confirm="delete-release <project-slug> <inactive-release-id>"Cleanup requires rollback permission. The active artifact is never removable. Add --allow-rollback-loss only when intentionally removing the active release's immediate predecessor; that removes the predecessor's runtime files and the active release's matching data-restore snapshot. Cleanup preserves release metadata and audit history.
Preview environments
clank preview deploy <name> creates or refreshes an expiring child environment without changing the directory's production link. The child receives an independent project ID, hostname, port, database, migration ledger, releases, secrets, jobs, logs, metrics, backups, and token namespace. Data starts empty and secret values are never copied. An explicit --data=sanitized request may branch only the rows and transforms frozen into the active production release; raw copies remain unavailable and pull-request code cannot change the trusted policy.
Previews consume normal account/workspace project capacity, cannot contain nested previews, and are grouped under their parent in the control-plane UI. Startup cleanup runs before release recovery, so an expired runtime is deleted rather than restarted. The background cleaner uses the same durable lock and path-safe complete deletion path as a manual removal. See Preview environments for the CLI, API, CI, and retention contracts.
GitHub pull-request automation uses workload identity rather than a long-lived deployment secret. clank preview github configure <owner/repository> binds an immutable GitHub repository ID and exact deploy/cleanup workflow paths, then generates pinned workflows. The public exchange accepts only GitHub-signed RS256 OIDC tokens for the configured repository, workflow, event, ref, and control-plane audience. A JWT ID is single-use, and the resulting token expires after 15 minutes with authority over exactly one pull-N preview. Federation is refused for a trusted in-process runtime because pull-request code requires provider/container isolation.
Site deletion
Owners and organization administrators can permanently reclaim a site slot from the dashboard or CLI:
clank project delete [project-id] \
--confirm="delete-site <project-slug>" \
--acknowledge-data-lossDeletion requires an account-wide browser session or CLI token. A project-scoped token is deliberately insufficient, including one with tokens permission. Browser deletion also passes the normal same-origin and CSRF checks. The exact slug-bound phrase and separate acknowledgement make accidental generic confirmation impossible.
A production project with active preview children returns PREVIEWS_EXIST; delete or expire those environments first. This prevents a parent-row cascade from leaving a supervised child process or child storage outside the control database.
The operation holds the same durable project lock used by deploy, rollback, release cleanup, and backup work. It rechecks current membership under that lock, stops the supervised application, validates that the platform project path and its parents are real directories rather than symbolic links, and removes the complete project root. Only then does one control-database transaction revoke active project tokens, remove orchestration placement/operation state, delete project metadata and its cascading domain/release/secret/log/metric/backup-schedule rows, and append a surviving project.delete audit event.
If filesystem validation or removal fails, project metadata remains and Clank attempts to restart the prior active release. If the later metadata transaction fails after files were removed, the API reports a fixed recovery-safe error and a retry completes cleanup. A successful response means the platform-managed local application database, releases, rollback snapshots, local or object-backed encrypted recovery points, remote runner artifacts, secrets, logs, metrics, domains, and scoped tokens are gone. External databases, manual copies, provider-retained object versions, Caddy certificate storage, and other operator-managed copies are not discovered or erased.
Workspace audit history
Every project and organization event carries durable organization attribution in the control database. The Activity dashboard and clank activity --json expose the same newest-first, cursor-paginated feed. Owners, administrators, and developers can read history only for organizations where their current role permits audit; viewers are excluded. Project-scoped tokens must include audit, are rechecked against current membership, and see only their project.
Project deletion removes application state but not its audit rows, organization attribution, actor identity, or safe metadata. The deleted target is therefore still visible through GET /api/audit after /api/projects/:id/audit naturally becomes unavailable. Upgrading an older control database adds the organization column in place and backfills it from live project rows or the project's recorded create/delete metadata.
The API is append-only, not a cryptographically signed transparency log. A trusted database administrator can still alter SQLite directly; export audit records to an independently controlled append-only sink when that threat is in scope.
Rollback
Code-only rollback is the default:
clank releases
clank rollback <release-id>The target runs against the current database and must pass health before activation. Use expand/contract migrations so earlier code tolerates the newer schema.
Data restore can lose newer writes, so it is constrained:
clank rollback <previous-release-id> \
--restore-data \
--confirm="restore my-project"It is available only for the immediately previous release with a pre-deploy backup.
Secrets
printf '%s' "$API_KEY" | clank secrets set API_KEY
clank secrets list
clank secrets delete API_KEYSecret names and timestamps are visible; values are never returned. Values use AES-256-GCM under the platform master key and are decrypted only for runtime injection.
Docker launches pass one name-only CLANK_RUNTIME_ENV_B64 envelope to the container. The host-side Docker client receives no application-named variables, so values such as DOCKER_HOST, LD_PRELOAD, proxy settings, or TLS settings cannot change which daemon or executable performs the launch. The in-container Node bootstrap decodes the environment, deletes the envelope, and then imports application code. No secret value is placed in a process argument. Privileged host/container administrators can still inspect runtime environment state. Platform-controlled names such as PATH, HOME, HOST, PORT, NODE_ENV, and TRUST_PROXY are reserved.
Secret changes take effect on the next release or supervised restart.
The local default creates a 0600 master-key file. Production should provide CLANK_PLATFORM_MASTER_KEY through separate secret management and back it up independently.
Runners
Production clank-platform defaults to the isolated hosting profile. That profile selects Docker when CLANK_RUNNER is unset and refuses to start with the process runner. Development defaults to the trusted profile for a zero-setup local experience.
The process runner is dependency-free and appropriate only when every deployer and application is trusted by the host operator. It must be selected deliberately in production:
CLANK_HOSTING_PROFILE=trusted CLANK_RUNNER=process clank-platformThe packaged control plane refuses public signup in this profile. Bootstrap and email-bound invitations remain available for a personal host or explicitly trusted cohort.
The Docker runner adds read-only root, capability dropping, no-new-privileges, non-root execution, PID/memory/CPU limits, a temporary filesystem, and narrow release/data mounts:
CLANK_HOSTING_PROFILE=isolated \
CLANK_RUNNER=docker \
CLANK_DOCKER_IMAGE=node:22-bookworm-slim \
clank-platformInvalid runner/profile values and an isolated/process mismatch fail before storage or a listener is opened. Containers improve isolation but are not perfect hostile-code sandboxes. High-risk public multi-tenancy should use microVMs or dedicated nodes, strict egress policy, image digests, and secret mounts or an external secret broker.
API outline
Device/public:
POST /api/device/startPOST /api/device/tokenPOST /__clank/auth/invited-register— personal- or workspace-scoped invitation account creation without public signupGET /livez— process livenessGET /healthz,GET /readyz, orGET /_clank/readyz— storage-backed control-plane readiness; the reserved path is evaluated before application-host ingress for hosted load balancers
Browser session:
GET /api/dashboardGET /api/usage?organizationId=<id>&month=YYYY-MM— retained workspace usage and effective traffic limits;GET|POST /api/admin/invitationsandDELETE /api/admin/invitations/:id— browser-only platform administrator control of personal signup invitations;GET /api/audit?limit=100&before=<event-id>&organizationId=<id>— role-filtered workspace history that survives project deletion;- project status, metrics, releases, logs, and domains;
- project and domain creation/removal with CSRF;
DELETE /api/projects/:idwith exact confirmation and explicit data-loss acknowledgement;GET /api/device/infoPOST /api/device/approvePOST /api/device/deny/__clank/auth/*
Bearer:
- account and token listing/revocation;
- workspace usage through an account-wide or matching workspace token; project tokens are denied;
- project creation/listing/status;
- owner/admin-only permanent project deletion using an account-wide token;
- release upload/history/rollback;
- release storage usage and confirmation-gated inactive artifact cleanup;
- logs, encrypted secrets, scheduled/manual backup operations, and audit events.
Managed edge:
GET /_clank/tls/ask— token-protected, constant-time Caddy certificate permission lookup.
See CLI, Migrations, Dashboard and domains, Platform security, and Self-hosting.
Staged secret rotation
The platform supports staged, encrypted secret versions through the existing secrets permission:
clank secrets stage PARTNER_KEY --from-env=NEW_PARTNER_KEY
clank secrets validate <rotation-id>
clank secrets rotations
clank secrets activate <rotation-id>
# Deploy or restart the application to consume the newly active version.
clank secrets rollback <rotation-id>stage also reads from stdin when --from-env is absent. Candidate and previous values are stored encrypted with the platform master key; APIs, CLI output, and audit entries contain only names, opaque revisions, states, and validation metadata. Activation changes the secret used by the next launch; it does not silently restart an application. Rollback restores the previous configured secret and likewise requires a deployment/restart to update running processes.
Programmatic hosts can supply openPlatform({ validateSecret: async ({ projectId, name, value, signal }) => ... }) to probe a replacement credential against a trusted service. The probe has a 10-second deadline. Without a probe, validation is explicitly labelled format-and-encryption; it does not establish that the remote service accepts the credential. Provider checks are labelled provider-check. Validation expires after 15 minutes, and an interrupted validation can be reclaimed after 15 seconds. A failed check cannot be activated.
Activation compares the exact secret revision captured at staging. An intervening secret update blocks stale activation; rollback similarly refuses to overwrite a newer change. A project keeps at most 1,000 rotation records. Deleting a secret removes its rotation history atomically, and project deletion cascades through all versions.
clank secrets rotations reports the current versions and those consumed by the live local runtime (web process and background services share launch secrets), or the provider generation confirmed running by the coordinator. A current: false entry identifies an older/unknown consumed version. No running consumer is reported for stopped apps or an unconfirmed provider generation. The provider comparison uses its frozen encrypted environment; external services outside Clank's runtime inventory are not automatically discovered.
The API exposes these routes:
GET/POST /api/projects/:id/secrets/rotations
POST /api/projects/:id/secrets/rotations/:rotationId/{validate|activate|rollback}Stage accepts { name, value }; lifecycle actions accept {}. Existing project authorization, browser CSRF, and audit behavior apply to every operation.