gitops as CI
README.md

sip #

sip is a minimal CI control-plane sketch. Git hook glue is implemented with Plan 9 rc; trusted control-plane behavior is implemented in Rust. It follows the design in 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. Deployment and trust-boundary guidance is in docs/deployment.md.

What exists #

  • hooks/pre-receive.rc rejects unprivileged writes to refs/ci/* and refs/workflows/*.
  • hooks/post-receive.rc records accepted branch and tag updates in $GIT_DIR/sip/queue. When refs/sip/orchestrator exists, it also runs the worker during the push and reports when a workflow ran.
  • bin/sip-worker.rc launches the Rust worker, which consumes queued events, peels refs to commit IDs, detects .sip/workflows, and writes manifest blobs to refs/ci/runs/<run-id>, plus latest status refs under refs/ci/status/heads/* or refs/ci/status/tags/*.
  • bin/sip-install-hooks.rc installs the hooks into a target repository.
  • The worker initializes $GIT_DIR/sip/logs/jobs.sqlite with workflow execution, job, dependency, and FTS5 log tables. When refs/sip/orchestrator points at a commit containing workflow.wasm or workflow.wat, the worker lets that module read selected workflow files and records declared jobs and dependencies.
  • examples/wasm-modules/basic is a Rust guest module for the first host ABI. It reads a small YAML subset with either a jobs: map or tasks: list.
  • examples/wasm-modules/unsafe-host-shell is the first executor module. It calls back into the host to run declared job commands through sh -c and record stdout/stderr in SQLite.

Usage #

The worker currently shells out to git and sqlite3; both must be available on the Git server PATH.

bin/sip-install-hooks.rc /path/to/bare.git
bin/sip-worker.rc /path/to/bare.git
bin/sip-logs.rc /path/to/bare.git [job-id]

sip-logs.rc prints log lines for a specific job ID. If no job ID is supplied, it prints the logs for the most recently written job log entry.

To exercise the WASM declaration scaffold:

cargo build --manifest-path examples/wasm-modules/basic/Cargo.toml --target wasm32-unknown-unknown
blob=$(git -C /path/to/bare.git hash-object -w examples/wasm-modules/basic/target/wasm32-unknown-unknown/debug/sip_basic_workflow.wasm)
tree=$(printf '100644 blob %s\tworkflow.wasm\n' "$blob" | git -C /path/to/bare.git mktree)
commit=$(printf 'orchestrator\n' | git -C /path/to/bare.git commit-tree "$tree")
git -C /path/to/bare.git update-ref refs/sip/orchestrator "$commit"

To execute declared jobs with the unsafe host shell executor:

cargo build --manifest-path examples/wasm-modules/unsafe-host-shell/Cargo.toml --target wasm32-unknown-unknown
SIP_EXECUTOR_MODULE=examples/wasm-modules/unsafe-host-shell/target/wasm32-unknown-unknown/debug/sip_unsafe_host_shell.wasm \
  bin/sip-worker.rc /path/to/bare.git

There is also a complete disposable example that creates a bare Git repository, pushes examples/test-repo, runs the worker, and prints the recorded jobs and logs:

examples/run-test-repo.rc

Tests #

tests/integration.rc

Deployment #

See 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 process on the server. Do not expose protected-ref writes through client environment variables.

Set SIP_ACTOR in the trusted Git wrapper before invoking git-receive-pack if you want queued events to record the authenticated actor.

Current boundary #

This is only the Git-event, state-recording, job-declaration, and first unsafe executor base. It does not expose secrets or upload artifacts. The unsafe-host-shell executor intentionally runs on the host and should only be used in trusted development/test contexts until policy and isolation are added.

Implementation Direction #

Plan 9 rc stays limited to Git hook glue, hook installation, and thin process launching. Trusted control-plane components should be implemented in Rust, including the Wasmtime host, capability enforcement, scheduler/job records, structured validation that outgrows the RC scripts, and any ref/state mutation logic that becomes security-sensitive. The installable server-side package should include the hooks, worker, and WASM runtime, preferably as a single binary where that stays practical. Do not introduce C for trusted system components.