211 lines
9.6 KiB
Markdown
211 lines
9.6 KiB
Markdown
# 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.14` on 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`, structured `identity.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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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.md`
|
|
- `identity.json`
|
|
- `identity-lock.json`
|
|
- `identity-manifest.json`
|
|
|
|
Then run:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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.
|