# FOG Local Podman PoC

Status: Runnable Fixture 0.2

Date: 2026-08-08

## 1. Purpose and Claim Boundary

`FOG-LOCAL-POC-1` is the first runnable local process, isolation, baseline,
and failure-test boundary for FOG. It turns the architecture's role and
reachability rules into a validated rootless Podman fixture.

The machine-readable artifacts and validator are in `../deploy/podman/`.

This definition is functional evidence only. It cannot demonstrate operator
independence, infrastructure diversity, an anonymity set, traffic-analysis
resistance, availability, capacity, production key handling, or a safe
numeric protocol profile. All containers run on one host under one user and
therefore share a host kernel, storage stack, management account, clock, and
failure domain.

The fixture does not activate FOG-WIRE, FOG-PKI, KEMSphinx, storage,
messaging, cover, delay, polling, retry, or degraded-mode parameters. It does
not implement or substitute for any protocol daemon. Its deliberately bounded
and non-cryptographic transport is named `FOG-POC-FIXTURE-1`.

## 2. Requirements

The PoC definition is governed by:

- `ARC-001`, separate trust domains even when co-located;
- `ARC-002`, a networkless Composer;
- `ARC-003`, no data-plane bypass;
- `ARC-005`, complete authenticated network views;
- `ARC-007`, one private-key owner and no shared writable state;
- `ARC-008`, bounded configuration, work, and state;
- `ARC-009`, no privacy-weakening fallback;
- `IF-06` through `IF-09`, exact online data-plane adjacencies;
- `TM-NET-03`, `TM-NET-04`, `TM-PKI-02`, `TM-PKI-03`,
  `TM-ROLE-01`, `TM-ROLE-02`, `TM-ROLE-03`, `TM-CRYPTO-01`, and
  `TM-AVAIL-01`;
- `PKI-INV-01`, `PKI-INV-03`, `PKI-INV-04`, and `PKI-INV-08`;
- `WIRE-INV-07`, `WIRE-INV-08`, `WIRE-INV-09`, and `WIRE-INV-10`.

Podman 5.8.3 and podman-compose 1.6.0 are present on the first local host. The
runtime contract targets rootless Podman and internal bridge networks. Podman
documents `network=none` as a network namespace without configured network
interfaces and an internal bridge as restricting external access. These
runtime properties remain acceptance-test subjects rather than assumptions.

## 3. Deployment Stages

### 3.1 Definition stage, completed

The checked-in JSON plan and standard-library Go validator define roles,
network edges, private state ownership, container hardening, resource
containment, and the mandatory fault matrix.

This stage prevented placeholder ports, fake cryptography, reusable example
keys, or an unreviewed generic fixture protocol from becoming accidental
implementation defaults.

### 3.2 Fixture stage, current

The executable harness provides explicitly named PoC-only binaries. They
exercise process lifecycle, exact opaque object geometry, network
reachability, bounded connections and work, restart behavior, role-local
state, secret isolation, and fault outcomes. They do not call their transport
FOG-WIRE or their public data FOG-PKI.

The stage builds one scratch image without build-time networking, addresses it
by its local OCI manifest digest, and deterministically generates a Compose
manifest from validated `topology.json`. The internal TCP port `17001` belongs
only to the fixture harness and is not a selected FOG protocol port.

### 3.3 Protocol-functional stage

Actual role executables replace the fixtures only after the relevant exact
profiles, libraries, parsers, key formats, and conformance vectors are
selected. Each replacement must retain the same state and network ownership
contract and pass the same fault matrix.

## 4. Role Inventory

| Instance | Executable contract | Purpose | Network state |
| --- | --- | --- | --- |
| `composer-fixture` | `fog-poc-composer-fixture` | Networkless public-fixture acceptance state | `none` |
| `authority-fixture` | `fog-poc-authority-fixture` | Networkless authority-condition state | `none` |
| `relay` | `fog-client-relay` | Opaque client queue and entry submission | private pairwise network |
| `entry` | `fog-entry` | Entry boundary outside the mix layers | two private pairwise networks |
| `mix-l1` | `fog-mix`, fixed layer 1 | First mix position | two private pairwise networks |
| `mix-l2` | `fog-mix`, fixed layer 2 | Second mix position | two private pairwise networks |
| `mix-l3` | `fog-mix`, fixed layer 3 | Third mix position | two private pairwise networks |
| `courier` | `fog-courier` | Terminal request and replica mediation | five private pairwise networks |
| `store-a` through `store-d` | `fog-store` | Four separate storage fixtures | one private pairwise network each |

The three-node description refers to the three fixed mix positions. Entry and
courier remain outside those three KEMSphinx delay layers. Four storage
containers exist so the PoC does not normalize a smaller shared storage
fixture into the intended architecture.

The authority and Composer fixtures are networkless process and state
boundaries. Fault flags model only coarse acceptance states such as quorum
loss or conflicting public fixtures. They do not create signatures,
consensus objects, identities, contacts, capabilities, or message secrets and
supply no authority-independence claim.

An observer is not in this first topology. `FOG-OBSERVABILITY-1` now defines
the separate structural logging and aggregate contract, but no observer,
numeric observability profile, or IF-11 codec is active in this fixture. No
container may substitute ordinary logs or packet traces for `fog-observer`.

## 5. Network Segmentation

Every declared network is internal and contains exactly two roles:

```text
relay <-> entry <-> mix-l1 <-> mix-l2 <-> mix-l3 <-> courier
                                                            |-- store-a
                                                            |-- store-b
                                                            |-- store-c
                                                            `-- store-d
```

The nine networks are:

| Network | Members | Interface |
| --- | --- | --- |
| `relay-entry` | relay, entry | `IF-06` |
| `entry-l1` | entry, mix-l1 | `IF-07` |
| `l1-l2` | mix-l1, mix-l2 | `IF-07` |
| `l2-l3` | mix-l2, mix-l3 | `IF-07` |
| `l3-courier` | mix-l3, courier | `IF-08` |
| `courier-store-a` | courier, store-a | `IF-09` |
| `courier-store-b` | courier, store-b | `IF-09` |
| `courier-store-c` | courier, store-c | `IF-09` |
| `courier-store-d` | courier, store-d | `IF-09` |

There is no shared data-plane bridge. No role uses the host network. No
container port is published to the host. The Composer and authority fixtures
use `network=none`. Coarse public-data failure conditions are written only to
the targeted role's private fixture state and contain no consensus bytes or
cryptographic material.

The runnable harness proves that containers cannot reach an
external address, host-local services, or undeclared roles. Name resolution
success is not sufficient evidence of an authorized protocol edge, and
failure of one edge must never create another.

## 6. Filesystem, Keys, and Container Hardening

Every role-container contract requires:

- rootless execution as a dedicated non-root container user;
- one digest-pinned immutable image;
- a read-only root filesystem;
- `no-new-privileges`;
- all Linux capabilities dropped;
- no privileged mode, device passthrough, host PID, host IPC, host UTS, or
  host network namespace;
- private `/run` and `/tmp` tmpfs mounts with bounded sizes in the generated
  manifest;
- exactly one role-owned writable state volume;
- no source checkout, container socket, generic host directory, or other
  role's state mounted;
- disabled core dumps and no secret-bearing diagnostic output;
- explicit CPU, memory, PID, file-descriptor, connection, and queue limits.

The checked-in CPU, memory, and PID values are harness containment limits, not
capacity results or protocol profile parameters. Their only purpose is to
make local fault tests bounded.

One 32-byte non-cryptographic fixture token per role is generated in memory
from the operating-system CSPRNG. It is streamed into a distinct Podman Secret
object. The installed `podman-compose` plus `runc` combination cannot mount
that file directly into a read-only role root filesystem, so a networkless,
bounded, one-shot stager copies it into a newly created role-specific volume
with mode `0400`. The Podman Secret source is removed immediately and the role
mounts only its own secret volume read-only.

Tokens never enter source, JSON plans, images, host-supplied files, command
arguments, environment templates, logs, test reports, or public fixture objects. A
constant-time runtime challenge confirms that each role received its intended
token without reporting a value or digest. Teardown removes the disposable
secret and state volumes. Destruction of the `mix-l2` replay fixture requires
the separate `--allow-destructive` option and names the exact target first.

## 7. Baseline Functional Flow

The runnable fixture performs these steps from clean role state:

1. Start the Composer and authority process fixtures without a configured
   network interface and validate their private state and token scope.
2. Generate one opaque fixed-size packet fixture with the current 16,150-byte
   candidate geometry, clearly labeled as non-cryptographic PoC data.
3. Admit the fixture only at relay, then traverse entry, mix-l1, mix-l2,
   mix-l3, and courier in that order.
4. Dispatch one bounded opaque operation from courier to the storage fixture
   set and collect only the modeled receipt result.
5. Return a bounded opaque result through the declared reverse adjacency,
   without creating a direct courier-to-relay connection.
6. Verify that no role mounted another role's state or token and that no
   container gained an undeclared network.
7. Report only the role names, scenario IDs, and coarse fixture outcomes.

The successful first-host result is `complete-fixture`, four modeled fixture
receipts, and four fixture hops after entry. These counts describe only this
test transport and must not be read as storage durability or anonymity
evidence.

The harness keeps no packet transcript. Container logging is disabled and no
result contains packet bytes, object identifiers, per-packet timing, keys,
route secrets, capabilities, or source addresses.

## 8. Fault Matrix

`fault-scenarios.json` defines fifteen mandatory scenarios:

| Scenario | Expected invariant |
| --- | --- |
| `forbidden-role-edge` | Only declared pairwise reachability exists |
| `entry-stop` | No relay-to-layer-1 fallback |
| `mix-layer-two-stop` | No layer skip or courier delivery |
| `layer-two-three-partition` | Bounded failure on the same adjacency |
| `mix-replay-state-loss` | Same-epoch processing fails closed |
| `stale-consensus` | New work stops at hard expiry |
| `same-epoch-consensus-fork` | Consumers freeze instead of choosing a view |
| `authority-quorum-loss` | Partial views and insufficient signatures fail |
| `clock-uncertainty` | Time-sensitive acceptance stops |
| `single-store-loss` | No false durability result |
| `receipt-quorum-loss` | Required receipts cannot be fabricated |
| `courier-stop` | No layer-3 or client connection to storage |
| `entry-handshake-flood` | Work and responses remain bounded |
| `cover-process-stop` | A cover-dependent claim cannot continue silently |
| `role-secret-isolation` | No cross-role state or fixture-token access |

Each scenario names target roles, the abstract action, expected behavior,
explicitly prohibited fallback, requirement IDs, automation status, and
whether it mutates role state. Abstract actions deliberately contain no shell
command. The fault runner resolves container and volume targets
from the validated plan, show destructive targets before execution, and start
state-destructive tests from disposable snapshots.

No scenario may run concurrently with another. Each begins from a documented
clean baseline so an earlier partition, stopped role, or modified volume
cannot make a later test pass for the wrong reason.

On the first host, all fourteen automatable scenarios pass. The
`cover-process-stop` row remains `not-automatable`: the fixture has no cover
scheduler and therefore cannot silently retain a cover or unobservability
claim. The effective runtime verifier completes 239 checks before the fault
matrix begins.

## 9. Acceptance Gates

### 9.1 Definition gate, satisfied

- The strict parser accepts the checked-in topology and fault plan.
- Duplicate or unknown fields, trailing JSON, oversized or non-regular inputs
  fail.
- Required roles, exact mix layers, four stores, pairwise internal networks,
  private state, private secret scopes, hardening flags, and resource bounds
  are enforced.
- Forbidden edges, host exposure, shared volumes, missing faults, and invalid
  traceability references fail validation.
- The validator uses only the Go standard library and performs no network or
  filesystem writes.

### 9.2 Runnable fixture gate, satisfied

- One local scratch image is built and referenced by its OCI manifest digest.
- PoC-only fixture roles and future online security roles have distinct
  executable entrypoints.
- The generated Compose manifest derives from validated `topology.json`.
- Effective Podman state, not only source YAML, passes 239 runtime checks.
- Rootless operation, pairwise internal networks, failed host and external
  probes, no published ports, private volumes, read-only roots, zero process
  capability sets, and role-token isolation pass.
- The baseline and all fourteen automatable fault scenarios pass.
- Reports contain only coarse outcomes. The cover-only row is explicitly not
  automatable and grants no cover claim.

### 9.3 Protocol-functional gate, not yet satisfied

- Replace fixtures with exact parsers, cryptographic profiles, key ownership,
  replay persistence, command policy, consensus transitions, and storage
  receipts from the normative specifications.
- Pass the PKI and FOG-WIRE functional PoC gates.
- Add parser conformance, fuzz, race, allocation, load, and mutation tests.
- Demonstrate current and next epoch transition, rollback refusal, revocation,
  per-record rekey, fresh handshakes, exact packet geometry, and strict
  failure behavior.

None of these gates creates an anonymity or production claim.

## 10. Validation Commands

From `deploy/podman/`:

```sh
gofmt -d cmd/fog-poc-plan/*.go internal/plan/*.go
go test ./...
go test -race ./...
go vet ./...
go run ./cmd/fog-poc-plan
go run -buildvcs=false ./cmd/fog-poc-lab -action all -allow-destructive
```

The expected summary is twelve roles, three mix layers, four stores, nine
internal pairwise networks, fifteen fault scenarios, and the claim
`functional-only`.

## 11. Deferred Decisions

This definition does not select:

- a Noise or hybrid post-quantum construction, library, profile ID, port,
  record size, timer, socket profile, or command shape;
- a PKI authentication suite, exact epoch schedule, topology shuffle,
  authority wire-key profile, or recovery ceremony;
- an active KEMSphinx suite or entry capsule;
- storage request geometry, durability profile, or receipt primitive;
- cover, delay, loop, retry, polling, queue, or shutdown distributions;
- observability fields, aggregation windows, release delays, or thresholds;
- a production container base image, release channel, deployment user, host
  firewall, backup, or update mechanism.

These remain explicit dependencies. The fixture generator supplies none of
them. Any future protocol-functional manifest generator must fail when a
required value is absent rather than converting fixture values into an
undocumented protocol default.

## 12. References

- FOG threat model: `FOG-THREAT-MODEL.md`
- FOG architecture: `FOG-ARCHITECTURE.md`
- FOG PKI: `FOG-PKI.md`
- FOG wire protocol: `FOG-WIRE.md`
- FOG traffic simulator: `FOG-SIMULATION.md`
- Podman run and network modes:
  <https://docs.podman.io/en/latest/markdown/podman-run.1.html>
- Podman internal network option:
  <https://docs.podman.io/en/v5.8.3/markdown/podman-network-create.1.html>
- Podman secret management:
  <https://docs.podman.io/en/stable/markdown/podman-secret.1.html>
