# ECP-0093: `ecp-forge` OP Stack Sepolia testnet and observation consensus rail

Status: Draft

## Problem / context

`every.channel` now has additive Ethereum-compatible commitments and signatures, but it does not yet
have:

- a concrete chain-operator path for bringing up a real OP Stack testnet on `ecp-forge`,
- a repo-owned bootstrap workflow that matches the current official OP Stack deployment guidance,
- an application-level consensus surface for "reality-derived" blocks where witnesses attest to the
  same observed broadcast epoch, or
- a reproducible smoke path that pushes real every.channel archive data through the rail.

The current `EveryChannelRail` contract is useful as a compact pointer store, but it does not
capture the consensus shape requested by the founder: nodes independently discover an observation
from reality, then witnesses sign off on that shared observation hash.

## Decision

Adopt a two-part rollout:

1. Stand up an OP Stack Sepolia-anchored testnet on `ecp-forge` using the official OP Stack stack:
   `op-deployer`, `op-geth`, `op-node`, `op-batcher`, `op-proposer`, `op-challenger`, and
   optional `op-dispute-mon`.
2. Introduce an application-level observation consensus rail for every.channel:
   - an `ObservationHeader` is derived from real stream epoch data,
   - witnesses attest to the derived observation hash,
   - the observation is finalized when quorum is reached,
   - full media bytes remain off-chain.

This keeps Ethereum as the ordering/finality substrate while making the application-level "block"
come from reality, not from arbitrary contract writes.

## Details

### `ecp-forge` OP Stack deployment

- Use the official OP Stack deployment topology described in the current Optimism chain-operator
  tutorial:
  - Sepolia L1 contracts via `op-deployer`,
  - `op-geth` + `op-node` sequencer,
  - `op-batcher`,
  - `op-proposer`,
  - `op-challenger`,
  - optional `op-dispute-mon`.
- Host these components on `ecp-forge` via repo-owned NixOS services and pinned official container
  images.
- Keep L2 RPC surfaces private by default; expose P2P only as required for testnet participation.
- Default L1 RPC / beacon endpoints may use public Sepolia providers, but deployment and operation
  require a repo-managed Sepolia private key secret with sufficient ETH.
- `op-challenger` and `op-dispute-mon` require a repo-managed Cannon absolute prestate artifact.
  If the prestate is absent, the core rollup may still run, but the dispute path is intentionally
  gated off instead of starting with incomplete configuration.

### Observation consensus

Introduce a new application-level surface alongside `EveryChannelRail`:

- `ObservationHeader`
  - `streamHash`
  - `epochHash`
  - `parentObservationHash`
  - `dataRoot`
  - `locatorHash`
  - `observedUnixMs`
  - `sequence`
- `ObservationHash`
  - `keccak256(abi.encode(ObservationHeader))`
- `ObservationSlot`
  - `keccak256(abi.encode(streamHash, epochHash))`

Witnesses attest to an `ObservationHash`, not directly to arbitrary payloads. This lets the chain
finalize the winning observation for a `(stream, epoch)` slot without storing the full manifest,
chunk list, PSIP data, or media bytes.

### Witness model

For the first testnet tranche:

- witness membership is registry-backed,
- quorum is fixed and explicit,
- no staking or slashing is required,
- sending an attestation transaction from a witness address is sufficient "sign off".

Off-chain EIP-712 witness signatures may be added later for relayed submission, but they are not a
prerequisite for the first live testnet.

### Real-data validation

Use real every.channel archive data already present on `ecp-forge` as the smoke source:

- derive observation headers from actual archive JSONL entries,
- submit them to a local Anvil deployment of the observation ledger,
- prove quorum/finalization against production-shaped data before attempting Sepolia rollout.

## Components

- `contracts/EveryChannelRail.sol`
  - compact manifest / transport pointer store (existing rail)
- `contracts/EveryChannelWitnessRegistry.sol`
  - witness membership for observation consensus
- `contracts/EveryChannelObservationLedger.sol`
  - candidate observation storage, attestation tracking, and finalization
- `scripts/op-stack/download-op-deployer.sh`
  - pinned `op-deployer` fetch helper
- `scripts/op-stack/setup-rollup.sh`
  - repo-owned OP Stack bootstrap/config generation
- `scripts/op-stack/anvil-reality-smoke.sh`
  - local smoke against Anvil using real `ecp-forge` archive data
- `nix/modules/ec-op-stack.nix`
  - NixOS service/module surface for `ecp-forge`

## Alternatives considered

- Use Base Sepolia directly as the only test environment. Rejected because it validates contract
  compatibility, but not operation of an every.channel-owned chain.
- Wait for a future custom consensus implementation before any chain bring-up. Rejected because it
  delays operator learning and hides infrastructure problems until late.
- Put media bytes or full manifests on chain. Rejected because it is operationally wasteful and not
  required for observation finalization.

## Rollout / teardown plan

1. Add the observation-ledger contracts and Foundry tests.
2. Add Anvil + real-archive smoke scripts.
3. Add an `ec-op-stack` Nix module and `ecp-forge` integration points.
4. Wire secrets for a Sepolia deployment key on `ecp-forge`.
5. Bring up the OP Stack services on `ecp-forge`.
6. Deploy observation-ledger contracts onto the new chain.
7. Submit real archive-derived observations and verify finalization.

Teardown:

- disable `services.every-channel.op-stack`,
- stop and remove the OP Stack containers/state directories,
- preserve the local Anvil smoke path and the additive Ethereum rail code.

## Open questions

- Should `ecp-forge` be the only sequencer for the first tranche, or should a second verifier-only
  peer be provisioned immediately?
- When we move beyond a registry-backed witness set, do we want bonds, reputation, or both?
- Should finalized observation slots bridge back into the existing manifest/transport pointer
  contract automatically, or remain a separate ledger surface for the first rollout?
