Status: Active | Owner: @roof | Applies to: main (2026-06) | Updated: 2026-06-11
ADR – 001 Store Images Are Signed and Verified by Cells
Context
The service store will accept community-submitted services. A cell installing a store service pulls a container image and runs it with access to the cell network. Without provenance guarantees, two attacks are possible:
- Malicious image content. An image that does not match its published source can carry malware onto every cell that installs it.
- Tag redirection. A manifest that references a mutable tag (
:latest) can be made to pull different content after review, even if the reviewed content was clean.
The store needed a way for a cell to decide, at install time, whether an image is exactly what the PIC publish process produced.
Options Considered
Option A — Trust the registry
Restrict images to git.pic.ngo/roof/* and rely on registry access control.
Rejected because: it protects against outsiders but not against a compromised publish path, a stolen registry credential, or tag mutation. The cell has no way to detect that an image changed after review.
Option B — Digest pinning only
Require every manifest image to be a @sha256: digest.
Rejected as insufficient alone: digests guarantee immutability but not origin. A digest written into a manifest by an attacker still installs cleanly. Digest pinning is kept as a necessary part of the solution.
Option C — Digest pinning + cryptographic signing, verified on the cell (chosen)
Every published store image is digest-pinned and signed with cosign. The cell verifies the signature against a bundled public key before starting any store container.
Decision
Store images must be digest-pinned and cosign-signed, and cells verify them at install time:
ServiceStoreManagerrejects manifests whoseimageis not a@sha256:digest.ServiceComposerrunscosign verifyagainst the bundled public key (config/cosign/cosign.pub) before bringing a service up (api/service_composer.py).- Behaviour is controlled by the
image_verificationsection incell_config.json:off|warn|enforce. The default isenforce— an undigested, unsigned, or signature-mismatched image refuses to install. If the verification mode cannot be read (corrupt config), the composer falls back toenforce: verification fails closed, never silently weakens. - Signing happens in the publish pipeline (images are signed and their digests written back into manifests before they reach the store index); the private key never exists on a cell.
Rollout was staged: verification shipped warn-by-default first, and the default flipped to enforce once every store image was signed.
Consequences
- Unsigned or undigested images refuse to install on any cell running the default
enforcemode. - Service developers must downgrade verification locally. To run images that have not been through the publish pipeline, set
image_verification.modetowarnoroffinconfig/api/cell_config.jsonand restart the API container. See Dev – Build a Store Service. - Manifests always reference digests, not tags. The
imagefield in a published manifest is a@sha256:digest, which removes tag-redirect attacks entirely. - Only the public key ships with PIC. Compromising a cell yields nothing that can sign new images.
- There is currently no API route or UI for the verification mode — changing it is a config-file edit. This is intentional friction: weakening verification should not be one click away.
Personal Internet Cell
New here?
Users
User – Connect to the VPN User – Use Your Services User – Troubleshooting
Admins
Admin – Overview Admin – Install and First Run Admin – Configure Domains and TLS Admin – Manage Services Admin – Configure Connectivity Admin – Manage Peers Admin – Back Up and Restore Admin – Logging and Audit Admin – Monitor and Troubleshoot
Developers
Dev – Overview Dev – Architecture Dev – Build a Store Service Dev – Service Manifest Reference Dev – API Reference Dev – Testing Dev – Install Internals
Decisions (ADRs)
ADR – 001 Store Images Are Signed and Verified by Cells ADR – 002 Named Connection Instances for Connectivity ADR – 003 All Optional Functionality Ships as Store Services
Meta
Meta – Glossary Meta – Template Runbook Meta – Template ADR
Archive
Archive – User Guide Archive – ADR 004 The Wiki Is the Single Documentation Source