# OP Stack on `ecp-forge`

This runbook covers the repo-owned OP Stack testnet surface introduced by [ECP-0093](/Users/conradev/Projects/every.channel/evolution/proposals/ECP-0093-ecp-forge-op-stack-sepolia-observation-testnet.md).

## Scope

- `ecp-forge` runs the every.channel OP Stack services with pinned official container images.
- The chain is Sepolia-anchored and private by default on the RPC side.
- Application-level consensus lives in the observation rail:
  - [EveryChannelWitnessRegistry.sol](/Users/conradev/Projects/every.channel/contracts/EveryChannelWitnessRegistry.sol)
  - [EveryChannelObservationLedger.sol](/Users/conradev/Projects/every.channel/contracts/EveryChannelObservationLedger.sol)

## Required inputs

- `secrets/op-stack-sepolia-private-key.age`
  - Sepolia operator key for `op-deployer`, `op-node`, `op-batcher`, and `op-proposer`.
- `secrets/op-stack-challenger-prestate.bin.gz.age`
  - Cannon absolute prestate artifact for `op-challenger`.

If the private key secret is absent, `services.every-channel.op-stack.enable = false` on `ecp-forge`.
If the prestate artifact is absent, `op-challenger` and `op-dispute-mon` stay disabled even when the
core rollup services are enabled.

## Local validation

Contracts:

```sh
nix shell .#foundry .#solc -c forge test -vv
```

Real archive data through Anvil:

```sh
nix shell .#foundry .#solc nixpkgs#jq nixpkgs#openssh nixpkgs#curl -c ./scripts/op-stack/anvil-reality-smoke.sh
```

The smoke script:

- deploys the witness registry and observation ledger to Anvil,
- reads a real archive JSONL entry from `root@git.every.channel`,
- derives `stream_hash`, `epoch_hash`, `locator_hash`, and `observation_hash`,
- finalizes the observation with two Anvil witnesses.

Default remote source:

- `/var/lib/every-channel/manifests/la-cbs/video0.m4s.jsonl`

Output artifact:

- `test-results/anvil-reality-smoke.json`

## Deploy

```sh
./scripts/deploy-ecp-forge.sh
```

The host bootstrap service is:

- `every-channel-op-stack-bootstrap.service`

It writes runtime state under:

- `/var/lib/every-channel/op-stack`

Key generated artifacts:

- `/var/lib/every-channel/op-stack/deployment.json`
- `/var/lib/every-channel/op-stack/sequencer/genesis.json`
- `/var/lib/every-channel/op-stack/sequencer/rollup.json`

## Verify

Core services:

```sh
ssh -o BatchMode=yes -o IdentityAgent=none -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 root@git.every.channel \
  'systemctl is-active every-channel-op-stack-bootstrap podman-every-channel-op-geth podman-every-channel-op-node podman-every-channel-op-batcher podman-every-channel-op-proposer'
```

Full stack including challenger/dispute monitor:

```sh
ssh -o BatchMode=yes -o IdentityAgent=none -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 root@git.every.channel \
  'systemctl is-active podman-every-channel-op-challenger podman-every-channel-op-dispute-mon'
```

Bootstrap outputs:

```sh
ssh -o BatchMode=yes -o IdentityAgent=none -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 root@git.every.channel \
  'jq . /var/lib/every-channel/op-stack/deployment.json'
```

## Contract deployment on the rollup

Once the rollup RPC is live, deploy the observation rail to the L2 RPC:

```sh
EVERY_CHANNEL_RPC_URL=http://127.0.0.1:28545 \
EVERY_CHANNEL_PRIVATE_KEY_FILE=/path/to/private-key \
./scripts/op-stack/deploy-observation-ledger.sh
```

## Notes

- `op-geth` and `op-node` RPC surfaces bind to `127.0.0.1` on `ecp-forge`.
- The OP Stack L2 execution RPC defaults to `127.0.0.1:28545`, not `8545`; `8545` is reserved for
  the full Ethereum mainnet node on the same host.
- `op-geth` P2P uses `28549`, not the Ethereum default `30303`; `30303` is reserved for the host
  full Ethereum node.
- The public firewall opening is only for the `op-node` P2P port.
- The bootstrap uses `op-deployer/v0.6.0-rc.3` by default and OP Labs runtime images aligned to the
  generated rollup schema.
