diff --git a/README.md b/README.md index b9877f3..458815a 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ the design in [ci-system-design-notes.md](ci-system-design-notes.md): Git hooks protect CI-owned refs, push events are durably queued, and a worker turns accepted branch/tag pushes into small manifests under `refs/ci/*`. The current durable record formats are defined in [docs/schemas.md](docs/schemas.md). +Deployment and trust-boundary guidance is in +[docs/deployment.md](docs/deployment.md). ## What exists @@ -76,6 +78,13 @@ examples/run-test-repo.rc tests/integration.rc ``` +## Deployment + +See [docs/deployment.md](docs/deployment.md) for hook installation, trusted +environment variables, privileged ref-write paths, queue/worker operation, +backup and retry behavior, and the security boundary between hooks, the Rust +worker, and user WASM modules. + Protected refs are rejected from the Git receive path. Server-side maintenance for `refs/workflows/*` and `refs/ci/*` must run outside `git-receive-pack`, for example with a local `git fetch` or `git update-ref` performed by an admin diff --git a/docs/deployment.md b/docs/deployment.md new file mode 100644 index 0000000..e762a56 --- /dev/null +++ b/docs/deployment.md @@ -0,0 +1,155 @@ +# Deployment and Trust Boundaries + +This document describes the current deployment model for `sip`. The project is +still a CI control-plane sketch, so treat these rules as the minimum safe shape +for development and test deployments rather than a production hardening guide. + +## Installed Components + +Install the server-side hooks into each bare repository that should emit CI +events: + +```sh +bin/sip-install-hooks.rc /path/to/bare.git +``` + +The installer writes: + +- `hooks/pre-receive`: rejects client writes to CI-controlled and unsupported + ref namespaces. +- `hooks/post-receive`: queues accepted branch and tag events under + `$GIT_DIR/sip/queue`, then invokes the worker when an orchestrator module is + installed. + +The worker and log reader are launched through thin wrappers: + +```sh +bin/sip-worker.rc /path/to/bare.git +bin/sip-logs.rc /path/to/bare.git [job-id] +``` + +The worker currently shells out to `git`, `sqlite3`, and, for the unsafe shell +executor, `sh`. These commands must be available in the trusted server PATH. + +## Trusted Environment + +Only the Git server wrapper, repository administrator, or service manager should +set `sip` environment variables. Do not allow clients to control these values +through push options or arbitrary receive-pack environment passthrough. + +- `SIP_ACTOR`: set by the trusted Git SSH/HTTP wrapper before + `git-receive-pack`. The post-receive hook records a sanitized value in queue + events. If unset, events record `unknown`. +- `SIP_WORKFLOW_REF`: optional worker override for the protected workflow ref. + The worker accepts only valid refs under `refs/workflows/*`; unsafe or + malformed values are ignored. The default is `refs/workflows/default`. +- `SIP_EXECUTOR_MODULE`: optional path to an executor WASM module. This is an + administrator-controlled runtime setting. The current example executor can run + host shell commands and must not be exposed to untrusted configuration. + +The installer substitutes `@SIP_ROOT@` in the post-receive hook with the local +checkout path. That path is trusted configuration because the hook uses it to +find `bin/sip-worker.rc`. + +## Privileged Refs + +Client pushes are accepted only for normal branch and tag activity. The +pre-receive hook rejects direct client writes to: + +- `refs/ci/*` +- `refs/workflows/*` +- all `refs/sip/*` except `refs/sip/orchestrator` +- `refs/notes/*` +- `refs/replace/*` +- unknown top-level ref namespaces + +Server-side maintenance for protected refs must happen outside the client +receive path, for example with local `git update-ref` or local `git fetch` +commands run by an administrator or service account on the Git server. + +`refs/workflows/*` stores protected workflow definitions. When a valid protected +workflow ref exists and contains `.sip/workflows`, it wins over workflow files +from the pushed worktree and records `workflow_trust=protected`. + +`refs/sip/orchestrator` stores the service-owned workflow WASM module as a Git +commit containing `workflow.wasm` or `workflow.wat`. Client pushes may update +this ref today, so deployments that need stricter separation should install it +only through trusted server-side maintenance or tighten the hook policy before +accepting untrusted users. + +`refs/ci/*` is service-owned output. The worker writes run manifests under +`refs/ci/runs/*` and best-effort latest status refs under +`refs/ci/status/heads/*` and `refs/ci/status/tags/*`. + +## Queue and Worker Operation + +The post-receive hook writes one tab-delimited event file per accepted branch or +tag update under: + +```text +$GIT_DIR/sip/queue/*.event +``` + +The worker initializes and uses these state directories: + +```text +$GIT_DIR/sip/queue +$GIT_DIR/sip/done +$GIT_DIR/sip/failed +$GIT_DIR/sip/logs +$GIT_DIR/sip/tmp +``` + +It processes queued events in filename order. Successful events move to +`sip/done`. Malformed events and events whose new object cannot resolve to a +commit move to `sip/failed`. If the worker exits with an error before moving an +event, the event remains in `sip/queue` and a later worker run will retry it. + +Job state and logs live in: + +```text +$GIT_DIR/sip/logs/jobs.sqlite +``` + +Back up `$GIT_DIR/sip` with the bare repository. The `queue`, `done`, and +`failed` directories are part of the audit trail and retry state. The `logs` +directory contains workflow execution rows, job rows, dependency rows, and +searchable job logs. The `tmp` directory contains transient checkouts and module +copies and does not need durable backup. + +## Security Boundary + +The current boundary is split across three layers: + +- Git hooks are trusted receive-path glue. They reject protected refs and create + event records, but should stay small and avoid complex policy. +- The Rust worker is the trusted control plane. It validates queue records, + resolves immutable object IDs, selects workflow sources, writes CI refs, + records SQLite state, and embeds Wasmtime. +- User workflow WASM is untrusted code. It can read selected workflow files and + declare jobs only through the host ABI exposed by the worker. + +The current Wasmtime host does not expose ambient WASI. Host functions mediate +workflow file reads and job declaration. Future host capabilities for artifacts, +network access, status updates, and secrets must enforce policy in Rust using +immutable event/workflow metadata, actor identity, workflow trust, and explicit +capability grants. + +The `unsafe-host-shell` executor is outside the safe boundary. It intentionally +runs declared commands with `sh -c` in an archived checkout and is suitable only +for trusted development or test deployments until executor isolation and policy +are added. + +## Current Gaps + +These are known deployment risks tracked as follow-up work: + +- Queue events are shape-validated but not authenticated. Protect + `$GIT_DIR/sip/queue` with service-owned permissions and add event signing or a + MAC before relying on queue provenance. +- Post-receive event writes are not yet atomic. +- Queued event refs need the same `git check-ref-format` validation used for + protected workflow refs. +- Pull and merge namespaces should remain rejected until their system-owned + semantics are defined. +- Wasmtime resource limits are not yet configured.