9.6 KiB
GCP leoclean no-send service package
Outcome
This slice packages the revision-bound Hermes/leoclean no-send artifact and one
explicit identity bundle as an immutable OCI image for a separate
leoclean-gcp-nosend.service on teleo-staging-1.
It is target-architecture staging work, not a production cutover. The image is the atomic compatibility boundary because Python, the locked Hermes virtual environment, PostgreSQL client, Cloud SQL CA, runtime artifact, identity files, and container entrypoint must move and roll back together.
Exact target and authority boundary
- project:
teleo-501523 - zone:
europe-west6-a - instance:
teleo-staging-1 - service account:
sa-teleo-staging-vm@teleo-501523.iam.gserviceaccount.com - service:
leoclean-gcp-nosend.service - platform:
linux/amd64 - image repository:
europe-west6-docker.pkg.dev/teleo-501523/teleo/leoclean-nosend-staging
The package adds only a deployable, digest-addressed staging service surface. It does not add IAM, Secret Manager, database-role, proposal-approval, canonical-apply, transport, production, or VPS authority.
The service has no provider credential in this slice. It can prove the gateway and database-tool boundary, but paid model calls require a separately reviewed internal no-send ingress and credential-delivery design during the parity slice.
Immutable closure
The dedicated Dockerfile uses only digest-pinned base images:
- Python
3.11.9; - uv
0.9.30; - PostgreSQL client
16.14on Bookworm.
There is no apt-get, floating pip install, editable install, or image tag in
the closure. The final image also binds:
- the exact Teleo Git revision and release-pass artifact receipt;
- Hermes commit and source-tree hash;
- the artifact content and manifest hashes;
- the exact uv lock;
- the synthetic identity manifest, lock,
SOUL.md, structuredidentity.json, original source hashes, and bundle hashes; - the exact no-send model, skills, tool registry, proposal-only database policy, and runtime-probe receipt that produced the identity runtime contract;
- the Cloud SQL server CA hash;
- the Dockerfile, entrypoint, and package-contract hashes.
The runtime image starts with a capability-limited bootstrap that creates a
fresh tmpfs profile, then drops all groups, UID/GID to 65532, and all Linux
capability bounding sets before executing Hermes. The gateway readiness receipt
is written only after the real zero-adapter runner starts and its sealed tool
registry is rechecked.
The PostgreSQL base image's inherited data-volume path is explicitly shadowed
by a tiny read-only, non-executable, mode-0000 tmpfs. This prevents Docker
from exposing an unused writable database directory to the runtime.
Prepare a build context
First compile and release-verify the no-send artifact from the exact clean revision, retaining it only after verification passes:
LEOCLEAN_NOSEND_ARTIFACT_OUTPUT=/tmp/leoclean-nosend-artifact \
scripts/run_leoclean_nosend_runtime_canary.sh
Derive the identity runtime contract from that artifact and receipt, generate a synthetic/noncanonical manifest from the committed fixture sources, and compile the two generated views:
python3 ops/gcp_leoclean_nosend_package.py identity-contract \
--artifact /tmp/leoclean-nosend-artifact \
--receipt .crabbox-results/leoclean-nosend-runtime.json \
--output /tmp/leoclean-nosend-identity-contract.json
python3 ops/gcp_leoclean_nosend_package.py generate-identity-manifest \
--artifact /tmp/leoclean-nosend-artifact \
--receipt .crabbox-results/leoclean-nosend-runtime.json \
--database-fingerprint fixtures/working-leo/leo-identity-v1/leo-database-fingerprint-v1.json \
--constitution fixtures/working-leo/leo-identity-v1/leo-constitution-v1.json \
--database-identity fixtures/working-leo/leo-identity-v1/leo-database-identity-v1.json \
--identity-source-root . \
--output /tmp/leoclean-nosend-identity-manifest.json
python3 ops/gcp_leoclean_nosend_package.py compile-identity \
--artifact /tmp/leoclean-nosend-artifact \
--receipt .crabbox-results/leoclean-nosend-runtime.json \
--identity-manifest /tmp/leoclean-nosend-identity-manifest.json \
--identity-source-root . \
--output /tmp/leoclean-nosend-identity
The compiled identity directory contains exactly:
SOUL.mdidentity.jsonidentity-lock.jsonidentity-manifest.json
Then run:
python3 ops/gcp_leoclean_nosend_package.py prepare \
--artifact /tmp/leoclean-nosend-artifact \
--receipt .crabbox-results/leoclean-nosend-runtime.json \
--identity /tmp/leoclean-nosend-identity \
--identity-source-root . \
--output /tmp/leoclean-nosend-image-context
The command fails closed on a development receipt, revision drift, content or
mode drift, unsafe links, an inconsistent identity lock, source or generated
view drift, a runtime/model/skills/permission mismatch, any canonical-authority
claim, a changed CA, a non-staging target, or an existing output directory. The
resulting context is secret-free. Direct canonical and staging-table writes
remain denied; proposal staging remains available only through
kb_stage.stage_leoclean_proposal(text,text,text,text,jsonb).
The Git gate is closure-specific: the Dockerfile, entrypoint, package contract,
Cloud SQL CA, identity validators, and manifest-derived synthetic identity
sources must be tracked regular files whose bytes, modes, staged state, and
SHA-256 values match the exact HEAD commit. Unrelated user worktree changes
remain permitted because they cannot enter this image context.
Use the exact values in build-args.json as Docker build arguments. The image
build itself revalidates that those values equal image-input.json.
Prepare one digest-bound release candidate
After an authorized build and Artifact Registry push returns the immutable image digest:
python3 ops/gcp_leoclean_nosend_package.py finalize \
--image-input /tmp/leoclean-nosend-image-context/image-input.json \
--image-reference \
europe-west6-docker.pkg.dev/teleo-501523/teleo/leoclean-nosend-staging@sha256:<64-hex-digest> \
--output-release /tmp/leoclean-nosend-release.json \
--output-unit /tmp/leoclean-gcp-nosend.service
This command validates the supplied digest syntax, target repository, expected
descriptor labels, and generated unit. It does not inspect Artifact
Registry and therefore does not attest that the remote digest actually has the
declared platform, labels, input hash, or source revision. The candidate unit
must not be installed until a separate read-only registry-inspection receipt
binds that exact digest to linux/amd64, its immutable config digest, the
required labels, the image-input hash, and the reviewed source revision.
Tags, alternate repositories, alternate projects/instances/service accounts, extra descriptor fields, and mismatched descriptor labels are rejected.
Offline evidence and live gate
Offline tests prove descriptor strictness, artifact and synthetic identity binding,
digest-only images, pinned runtime versions, a secret-free context, the
capability-drop contract, no published ports, no Docker-socket mount, no
environment file, and no production/VPS/Telegram target. CI also builds exactly
one linux/amd64 image, starts it with the reviewed restrictions, requires the
built-in zero-adapter/no-send_message health response, checks that an
unsupported send-message entrypoint command fails with the exact redacted
error, and binds the running container to the dynamically resolved image ID.
Before startup, the smoke also requires the built image to declare the implicit
root bootstrap user, exact Python entrypoint, service command, healthcheck
timings, and SIGTERM stop signal. The successful health command then proves
PID 1 dropped to UID/GID 65532 with zero Linux capabilities. Finally, the
smoke removes the candidate container, tag, and visible image ID. The receipt
records whether linux/amd64 ran natively or through cross-platform emulation.
Pinned base images and build cache may remain.
The entrypoint command rejection is not a real Hermes tool invocation, and the
bridge-network smoke does not prove general network-egress denial. These tests
also do not prove canonical identity, Cloud SQL permissions, Artifact Registry
digest contents,
or that teleo-staging-1 currently has a compatible container
engine, enough disk, the expected architecture, the expected service account,
or an unused unit name. Those are the next read-only live preflight. If the
engine is absent, installation is separate infrastructure work; the deploy must
stop instead of falling back to the production sync script or a mutable host
virtual environment.
Publishing the draft branch and stacked PR is only a review handoff. The next deployment-evidence slice must add immutable Artifact Registry inspection and make installation fail closed without its receipt. Live installation, Artifact Registry publication, service restart, IAM/secret changes, Cloud SQL access, and proposal staging remain separate approvals.
Rollback and sunset
The rollback boundary is one release descriptor, one image digest, and the dedicated no-send unit. Rollback restores the prior descriptor/unit and prior digest; it must never restart or edit the production Leo, Telegram, or VPS services.
The fresh tmpfs profile intentionally has no session continuity. That is appropriate for isolated gateway/database and fresh-turn parity proof, but it is not the production state design. Before traffic cutover, a separate reviewed slice must bind durable noncanonical session state without making it identity or knowledge authority. The tmpfs-only state contract sunsets when that cutover candidate is proven.