# Security and data contract (/operations/security-and-data)



# Security and data contract

This is the current implementation contract for security boundaries and data
handling. It is an evidence map, not a certification or a promise that every
host, guest, dependency, or operator configuration is secure. “Design-only
limit” means the reviewed implementation does not prove the stronger property.

> Navigation: [← Observability](/operations/observability) · [Index](/) · [Next: Development →](/guides/development)

## Security boundary

* Every job is admitted transitively before container, checkout, cache mutation,
  runtime credential injection, service, or download side effects. The
  run-service access token needed to acquire and renew the job is extracted
  before admission. Admission itself is read-only, bounded, and rejects unsafe
  or unresolved action structure.
  `crates/velnor-runner/src/admission.rs:1-14`,
  `crates/velnor-runner/src/admission.rs:485-530`

* Packaged services force strict capability validation. The packaged release,
  compiled capability manifest, and microVM artifacts are checked for identity
  coherence before activation/start.
  `crates/velnor-runner/debian/velnor.env:78-90`,
  `crates/velnor-runner/debian/postinst:174-205`

* Execution has two materially different trust boundaries. Firecracker uses one
  jailed VMM per job and guest-local Docker; the Docker backend uses the host
  Docker socket and runs as root.
  `crates/velnor-runner/src/execution/firecracker.rs:1-22`,
  `crates/velnor-runner/src/execution/firecracker.rs:184-255`,
  `crates/velnor-runner/src/execution/docker.rs:1-17`,
  `crates/velnor-runner/debian/velnor-daemon.service:62-64`

* The Docker lease proxy injects ownership labels and cgroup parents and
  rejects privileged mode, host binds, capabilities, and devices by default.
  This is ownership/reclaim control, not full Docker Engine authorization or
  tenant isolation. `VELNOR_ALLOW_PRIVILEGED_OPTIONS=true` can widen options for
  trusted jobs and must be treated as an explicit host-trust decision.
  `crates/velnor-runner/src/docker_lease.rs:1125-1303`,
  `crates/velnor-runner/src/github_adapter.rs:686-840`

## Data classes and handling

| Data | Live handling | Durable handling | Contract and limit |
|---|---|---|---|
| Workflow/action inputs and action metadata | Read during admission through a read-only metadata source; metadata and context have byte, node, depth, visit, and time bounds. | Metadata cache may retain bounded metadata; admission itself must not create job side effects. | Received values are excluded from admission capability errors; unresolved runtime roots remain unresolved until execution. `crates/velnor-runner/src/admission.rs:350-365`, `crates/velnor-runner/src/admission.rs:466-530`, `crates/velnor-runner/src/admission.rs:1049-1155` |
| Credentials, tokens, and secret references | Runtime environment can carry `GITHUB_TOKEN`, runtime access tokens, OIDC request tokens, and secret-derived expressions to the executor. | Sanitized DTOs carry secret names only; control-store events reject secret keys and known token markers. Runner OAuth/JIT material, including an optional private key, can also be persisted in `runner.json` with mode `0600`. | The runtime token values are sensitive live job data. Sanitization and file mode do not mean the executor, process environment, or host filesystem is secret-free or encrypted. `crates/velnor-runner/src/runtime_env.rs:132-197`, `crates/velnor-runner/src/runtime_env.rs:330-448`, `crates/velnor-runner/src/config.rs:59-79,168-203`, `crates/velnor-control/src/store/records.rs:1508-1600`, `crates/velnor-model/src/sanitized.rs:1-7` |
| Repository URLs and identity | Repository/workflow/ref/SHA identity is validated and carried through admission, guest messages, provenance, and sanitized records. | Sanitized URLs strip userinfo; opaque credential-bearing schemes are rejected. | Identity is metadata, not a secret. `crates/velnor-model/src/sanitized.rs:42-60`, `crates/velnor-runner/src/admission.rs:618-713` |
| Workspace, artifacts, and command output | Job work roots, scratch disks, stdio, command files, and result exports are handled by the selected executor. Firecracker checks message identity, nonce, plan hash, and result digest. Checkout with `persist-credentials` can write auth into `.git/config`; cleanup is best effort. | Workspaces/artifacts may remain in runner storage until cleanup or operator action. | Result-integrity checks do not establish confidentiality or secure deletion. `crates/velnor-runner/src/execution/firecracker.rs:847-1050`, `crates/velnor-runner/src/storage.rs:37-75`, `crates/velnor-runner/src/checkout.rs:288-320,597-620` |
| CAS objects and trees | Content-addressed objects are streamed, size-bounded, digest-verified, and materialized without symlink traversal. | Objects use BLAKE3 paths; trees retain relative paths, digests, and modes; unreachable objects can be reclaimed. | CAS integrity and path safety are provided; encryption at rest is not established. `crates/velnor-cas/src/lib.rs:1-40`, `crates/velnor-cas/src/lib.rs:370-535`, `crates/velnor-cas/src/lib.rs:967-1084`, `crates/velnor-cas/src/lib.rs:2011-2058` |
| Persistent caches | Cache authority is derived from admitted cache-contract steps, trust scope, repository, workflow, job class, and bounded reservations. Workflow overrides are not accepted. | Canonical cache paths are trust-scoped, with current code still able to read an existing legacy path. | Trust scope is a sharing boundary, not cryptographic confidentiality. `crates/velnor-runner/src/runtime_env.rs:219-313`, `crates/velnor-runner/src/storage.rs:77-127` |
| Control state and lifecycle events | Store writes are instance-namespaced and state/event changes are atomic. | `/var/lib/velnor/state.db` contains durable lifecycle state and append-only events; query, log, and storage projections are currently created in memory and are not repopulated after service reopen. Raw job messages, credentials, masks, and logs are excluded by contract. | Event text/detail are bounded and reject secret keys, markers, control characters, and bidi controls. `crates/velnor-control/src/store/mod.rs:1-10`, `crates/velnor-control/src/store/mod.rs:40-40`, `crates/velnor-control/src/store/records.rs:1-5`, `crates/velnor-control/src/store/records.rs:1508-1633`, `crates/velnor-control/src/application.rs:220-254` |
| Action execution journal | Records action identity, producer/consumer references, state, timing, output digests, worker, and trust class; telemetry exposes digests and safe identifiers. | SQLite WAL with `synchronous=FULL`, checksummed immutable records, leases, consumers, retention, termination claims, and trust revocations. | The journal is an integrity/recovery record, not an output-content vault. Its physical path and host filesystem permissions remain deployment concerns. `crates/velnor-action-journal/src/lib.rs:1-7`, `crates/velnor-action-journal/src/lib.rs:95-116`, `crates/velnor-action-journal/src/lib.rs:269-377` |
| Logs, forensic records, and traces | Forensic lines include identity and messages; tracing emits JSONL and may export OTLP to the configured endpoint. | Rotating files live below the configured logs directory; forensic and trace rotation retains one `.1` generation. | The reviewed writers are best-effort and show no general secret-redaction filter. `crates/velnor-runner/src/slot_log.rs:1-12`, `crates/velnor-runner/src/slot_log.rs:77-181`, `crates/velnor-runner/src/telemetry.rs:1-13`, `crates/velnor-runner/src/telemetry.rs:101-159` |
| Attestation and provenance | Approved `dist` subjects are streamed and SHA-256 hashed; OIDC claims are validated; bundles are uploaded to GitHub and signed through public-good or private endpoints according to repository visibility. | Bundle files are written below a unique runner-temp subdirectory and their container paths are recorded. | Attestation discloses repository, workflow, ref/SHA, run identifiers, runner environment, and subject names/digests to its configured services. `crates/velnor-runner/src/attestation.rs:53-142`, `crates/velnor-runner/src/attestation.rs:252-320`, `crates/velnor-runner/src/attestation.rs:427-566` |
| Package configuration and installation data | Debian units load operator config and, where applicable, an operator-owned secrets file. Guardian intentionally omits secrets. | `/etc/velnor/secrets.env` is operator-owned and mode `0600`; package configuration migrates a recognized token but does not ship or overwrite the secrets file. | Several runner units run as root with bounded systemd protections and explicit writable paths. `crates/velnor-runner/debian/postinst:156-172`, `crates/velnor-runner/debian/velnor-daemon.service:20-80`, `crates/velnor-runner/debian/velnor-guardian.service:6-35` |

## Threat-control table

| Threat | Current control | Residual/design-only limit | Evidence |
|---|---|---|---|
| Workflow smuggles an unapproved action, ref, path, input, or nested composite | Closed manifest, exact SHA/ref rules, path/input validation, recursion/cycle/depth/node/visit limits, and admission before side effects. | The manifest is the policy authority; an approved capability can still be dangerous by design. | `crates/velnor-runner/src/manifest.rs:785-896`, `crates/velnor-runner/src/manifest.rs:906-1133`, `crates/velnor-runner/src/admission.rs:725-883`, `crates/velnor-runner/src/admission.rs:901-1068` |
| Malformed or oversized remote metadata causes resource exhaustion | Response, metadata, context, input, graph, depth, traversal, and deadline bounds. | Bounds are availability controls, not confidentiality controls. | `crates/velnor-runner/src/admission.rs:35-68`, `crates/velnor-runner/src/admission.rs:466-530`, `crates/velnor-runner/src/admission.rs:1272-1345` |
| Action replay or stale guest message crosses a job boundary | Guest identity includes job/isolation/generation/nonce/plan hash; duplicate steps, replay, and terminal ordering are rejected. | This proves protocol checks in the reviewed adapter, not a general host compromise defense. | `crates/velnor-runner/src/execution/firecracker.rs:350-435`, `crates/velnor-runner/src/execution/firecracker.rs:847-1050` |
| MicroVM guest escapes through host Docker or shared network | Jailed Firecracker, read-only rootfs, writable per-job disk, guest-local Docker socket, per-VM TAP/netns, and default-drop forwarding rule. | The source proves this configuration path, not kernel/KVM, image, guest-agent, or host-hardening security. | `crates/velnor-runner/src/execution/isolation.rs:50-133`, `crates/velnor-runner/src/execution/firecracker.rs:642-710`, `crates/velnor-runner/src/execution/net.rs:1-111` |
| Host-Docker job reaches the host or another job | Host Docker backend requires the host socket and verifies systemd-v2 job-slice CPU quota. The packaged standalone job unit has a control-group kill boundary, but normal controller-spawned workers are not proven to use that unit template. | This is a root/host-Docker trust boundary, and the reviewed check establishes CPU quota, not microVM-equivalent filesystem, memory, network, or socket isolation. | `crates/velnor-runner/src/execution/docker.rs:21-33`, `crates/velnor-runner/src/execution/docker.rs:119-203`, `crates/velnor-runner/debian/velnor-job@.service:6-24`, `crates/velnor-runner/src/node/controller.rs:2248-2290` |
| Cache poisoning or cross-trust reuse | Cache IDs and declarations are generated from admitted contracts; scope must be trusted and owned by the repository; storage adds trust scope/class. | Existing legacy cache paths remain readable when canonical paths do not exist; no encryption or content confidentiality is shown. | `crates/velnor-runner/src/runtime_env.rs:219-313`, `crates/velnor-runner/src/storage.rs:77-127` |
| Tampered CAS object or unsafe tree path | BLAKE3 verification, atomic publication, regular-file checks, no symlink/traversal paths, bounded tree/file/total sizes, and fail-closed reclaim inventory. | Digest integrity does not prove origin authenticity or secrecy. | `crates/velnor-cas/src/lib.rs:1-40`, `crates/velnor-cas/src/lib.rs:230-350`, `crates/velnor-cas/src/lib.rs:1035-1084`, `crates/velnor-cas/src/lib.rs:2011-2058` |
| Secret leaks into sanitized state or structured errors | Secret references retain names only; URLs strip userinfo; control events reject secret keys/markers; admission and capability violations redact received values; error envelopes expose stable class/reason/remediation fields. | These are construction/validation controls for named surfaces, not a universal sanitizer for arbitrary logs, child processes, or files. | `crates/velnor-model/src/sanitized.rs:1-7`, `crates/velnor-model/src/sanitized.rs:42-60`, `crates/velnor-model/src/sanitized.rs:104-128`, `crates/velnor-runner/src/admission.rs:264-328`, `crates/velnor-runner/src/manifest.rs:85-108`, `crates/velnor-model/src/error_envelope.rs:115-158` |
| Log or telemetry exfiltration | Files and OTLP are explicit, bounded, rotating sinks; control-store events have a separate sanitizer. | Slot forensics and tracing accept message data, are best-effort, and show no equivalent general redaction. OTLP sends to an operator-configured endpoint. | `crates/velnor-runner/src/slot_log.rs:77-181`, `crates/velnor-runner/src/telemetry.rs:1-13`, `crates/velnor-runner/src/telemetry.rs:140-159`, `crates/velnor-control/src/store/records.rs:1508-1600` |
| Crash, lease loss, or concurrent retention deletes the wrong control data | SQLite WAL/foreign keys, bounded immediate transactions, lease generation fencing, commit fence, exact ownership, and rollback before commit. | A committed deletion is durable; post-commit maintenance failure is reported as post-commit and must not be retried as rollback. | `crates/velnor-control/src/store/retention.rs:710-765`, `crates/velnor-control/src/store/retention.rs:911-916`, `crates/velnor-control/src/store/retention.rs:1192-1309` |
| Unbounded control-store growth | Event/job age and row limits, database budget, bounded prune batches, physical free-space reserve, checkpoint/vacuum limits, and admission reservations. | Retention is bounded and may defer; it is not a promise of immediate deletion or a filesystem-wide quota. | `crates/velnor-control/src/store/records.rs:186-210`, `crates/velnor-control/src/store/retention.rs:29-81`, `crates/velnor-control/src/store/retention.rs:177-205`, `crates/velnor-control/src/store/retention.rs:1323-1477` |
| Orphaned workspace/image consumes or destroys active data | Reclaim targets UUID job workspaces and dangling untagged images only; warm caches, volumes, builders, and shared microVM work roots are protected or skipped. | Reclaim depends on the backend/lease conditions; it does not scrub bytes. | `crates/velnor-runner/src/leftover_disk.rs:1-10`, `crates/velnor-runner/src/leftover_disk.rs:103-154`, `crates/velnor-runner/src/leftover_disk.rs:222-238`, `crates/velnor-runner/src/leftover_disk.rs:279-364` |
| Package upgrade mixes executable, manifest, or microVM identities | Post-install drains active units, takes the package lock, verifies artifact SHA-256 values and binary/manifest coherence, and never restarts the fleet itself. | Operational activation and service enablement remain operator-controlled. | `crates/velnor-runner/debian/postinst:4-15`, `crates/velnor-runner/debian/postinst:92-147`, `crates/velnor-runner/debian/postinst:174-247` |
| Detached action is incorrectly reused or physically terminated | Adoption requires identical action identity and adoptable policy; consumer attach/detach is durable; trust revocation bypasses retention; physical termination has one durable claim. | Supersession is disabled by default; the physical terminator is an external hook. | `crates/velnor-action-journal/src/supersession.rs:1-6`, `crates/velnor-action-journal/src/supersession.rs:32-90`, `crates/velnor-action-journal/src/supersession.rs:339-492`, `crates/velnor-action-journal/src/supersession.rs:774-891` |

## Storage and lifecycle rules

The packaged storage roots are `/var/cache/velnor/v1`, `/var/lib/velnor`,
`/run/velnor`, and `/var/log/velnor`; user-mode storage uses XDG locations and
does not default to `$HOME/.velnor`. The control database is
`/var/lib/velnor/state.db`. `crates/velnor-runner/src/storage.rs:37-75`,
`crates/velnor-control/src/store/mod.rs:40-40`

The lifecycle is:

1. Admission reads and validates the complete action closure.
2. Execution receives the admitted plan and may create workspaces, scratch
   disks, caches, artifacts, logs, and runtime credentials.
3. Durable control state/events and action state retain bounded sanitized
   records, digests, identities, leases, and events; query, log, and storage
   projections are currently transient after service construction. Content
   stores retain content only where their respective paths are used.
4. Teardown removes the exact executor resources. Orphan reclaim and control
   retention are separate bounded maintenance paths.

Firecracker teardown explicitly does not imply secure wipe. CAS reclaim removes
unreachable objects, and leftover-disk reclaim removes selected filesystem
objects/images; neither source establishes media sanitization.
`crates/velnor-runner/src/execution/firecracker.rs:402-435`,
`crates/velnor-cas/src/lib.rs:1035-1084`,
`crates/velnor-runner/src/leftover_disk.rs:184-226`

## Explicit design-only limits

* **No at-rest confidentiality claim.** The reviewed sources do not establish
  encryption for the control database, action journal, CAS, caches, workspaces,
  logs, attestation bundles, snapshot files, or writable disks. File modes and
  systemd path restrictions are not encryption.
  `crates/velnor-cas/src/lib.rs:2011-2058`,
  `crates/velnor-action-journal/src/lib.rs:269-312`,
  `crates/velnor-runner/src/telemetry.rs:25-73`
* **No secure-erasure claim.** Unlink, reclaim, teardown, rotation, and SQLite
  deletion are logical cleanup only. Recovery from storage media, snapshots,
  filesystem slack, backups, and host-level observability is outside this
  contract.
  `crates/velnor-runner/src/execution/firecracker.rs:402-435`,
  `crates/velnor-control/src/store/retention.rs:710-765`
* **No Docker/microVM equivalence claim.** The Docker backend is a root process
  with access to the host Docker socket; its reviewed boundary check is a
  cgroup CPU quota. Select Firecracker when the host-Docker trust boundary is
  unacceptable.
  `crates/velnor-runner/src/execution/docker.rs:1-17`,
  `crates/velnor-runner/src/execution/docker.rs:119-203`,
  `crates/velnor-runner/debian/velnor-daemon.service:62-80`
* **No secret-zeroization claim.** Runtime environment injection is intentional;
  the credential-free guest handshake is a precondition for snapshot creation,
  not proof that all secret-bearing memory, files, process environments, or
  logs have been erased.
  `crates/velnor-runner/src/runtime_env.rs:132-197`,
  `crates/velnor-runner/src/execution/firecracker.rs:508-585`,
  `crates/velnor-runner/src/execution/firecracker.rs:746-821`
* **No universal log-redaction claim.** Control-store event validation is
  stronger than the reviewed forensic/tracing writers. Do not send secrets or
  untrusted sensitive payloads to those sinks.
  `crates/velnor-control/src/store/records.rs:1546-1600`,
  `crates/velnor-runner/src/slot_log.rs:77-181`,
  `crates/velnor-runner/src/telemetry.rs:101-159`
* **No immediate-global-retention claim.** Control retention has bounded defaults
  and may defer under lease, deadline, busy, reserve, or maintenance failure;
  it does not govern CAS, cache, workspace, log, package, or external service
  retention.
  `crates/velnor-control/src/store/retention.rs:177-205`,
  `crates/velnor-control/src/store/retention.rs:1323-1477`,
  `crates/velnor-runner/src/leftover_disk.rs:222-238`
* **No complete network-policy claim for every backend.** The reviewed default-
  drop TAP/netns policy is the microVM path. The Docker source does not provide
  an equivalent per-job network-control statement.
  `crates/velnor-runner/src/execution/net.rs:1-111`,
  `crates/velnor-runner/src/execution/docker.rs:119-203`
* **No universal guest credential-free claim.** The guest handshake rejects the
  specifically checked GitHub/Actions environment names, but it does not scan
  files, memory, or every possible credential variable. Guest-local Docker is
  root-owned, so isolation relies on the VM boundary; guest CPU/memory limits
  are not established by the reviewed jailer path.
  `crates/velnor-runner/src/execution/guest_agent.rs:39-64`,
  `crates/velnor-runner/src/execution/guest_image.rs:84-103`,
  `crates/velnor-runner/src/execution/firecracker.rs:203-225,654-686`
* **No vsock confidentiality or peer-authentication claim.** The host/guest
  protocol checks identity, nonce, and digest values over AF\_VSOCK, but the
  reviewed protocol does not encrypt or independently authenticate the peer.
  Trust depends on Firecracker and host isolation.
  `crates/velnor-runner/src/execution/guest_agent.rs:492-535`,
  `crates/velnor-model/src/vsock_protocol.rs:146-215`
* **No JIT-secret erasure claim.** Runner OAuth/JIT material, including an
  optional private key, can be persisted in `runner.json` with mode `0600`;
  file mode is not encryption. Checkout credential cleanup is best effort.
  `crates/velnor-runner/src/config.rs:59-79,168-203`,
  `crates/velnor-runner/src/checkout.rs:597-620`
* **No built-in physical supersession guarantee.** Supersession defaults off,
  retention is bounded, and the actual physical terminator is supplied outside
  this journal module.
  `crates/velnor-action-journal/src/supersession.rs:32-90`,
  `crates/velnor-action-journal/src/supersession.rs:138-142`
