# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working in this repository.

## What this is

`fialucci-oracle` is the **opinionated SDK** external authors use to build Fialucci oracles. The chain is `fialucci-chain.git`; the protocol types are `fialucci-types` from crates.io; this crate sits one level above both, owning the network protocol so wire shapes stay byte-identical to the chain by construction.

The single hard constraint: **devs cannot change how the oracle interacts with the network.** Signing, body construction, HTTP submission, observation-request polling — all SDK-owned. The dev-facing surface is one trait, [`OracleHandler`], with one method, `observe`.

## Architecture

Five modules, each one concern:

- **`identity.rs`** — `OracleIdentity`. ed25519 keypair + derived `OracleId` + keystore JSON I/O. `sign_body` is **crate-private** — there is no public method that signs arbitrary bytes. Everything else flows through the runtime, which is the only place `sign_body` is called.
- **`proof_body.rs`** — `ProofBodyBuilder`. Fluent constructor for `OracleDataSubmissionBody` (the type from `fialucci-types`). Used internally by the runtime; also exposed to devs for one-off use in tests.
- **`handler.rs`** — `OracleHandler` trait + the dev-facing `ProofClaimEvidence` return type. The only place dev code touches the protocol.
- **`client.rs`** — `ChainClient`. Narrow HTTP wrapper: `/chain-info`, `/oracle/submissions`, `/oracle/observation-requests/matching`, `/oracle/observation-requests/{id}/submissions`. Nothing else.
- **`runtime.rs`** — `Oracle::builder().run()`. Polls, dispatches, signs, submits, sleeps, repeats.

### Why crate-private signing matters

Three things must be byte-identical between every oracle and the chain:
1. Proof body canonical JSON.
2. Signing bytes (`FIALUCCI_ORACLE_PROOF_V1\n || body_json`).
3. ed25519 signature.

If a dev can re-implement any of those, drift becomes a question of when. So the SDK calls `fialucci_types::sign_oracle_data_submission` directly — the same authoritative function the chain's dev-tool binaries use — and the dev never sees the call. That's enforced by `OracleIdentity::sign_body` being `pub(crate)`.

## Essential commands

```bash
cargo build
cargo test
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo run --example manual_oracle -- <node-url> <oracle-key-file>
```

Toolchain: stable Rust (matches `fialucci-chain`'s pinned 1.95.0; no separate `rust-toolchain.toml` here yet).

## Conventions

- **Wire compatibility is the contract.** The SDK and the chain depend on the same `fialucci-types` version (currently `0.0.2`). Bumping the `fialucci-types` minor here is a potentially-breaking SemVer event — track upstream releases.
- **No public signing primitive.** If you add an internal API that signs bytes, keep it `pub(crate)`. The dev-facing surface is `OracleHandler::observe` and only that.
- **No I/O on hot paths in the handler.** The runtime drives ticks; handlers should be quick — long-running work belongs in a separate task the handler awaits.
- **`hex::encode` / `hex::decode` for display.** Same convention as `fialucci-types`.

## Downstream consumers

| Crate / repo | What it uses |
|---|---|
| External oracle apps (anyone's) | `Oracle`, `OracleHandler`, `OracleIdentity`, the re-exported `types` |
| [`fialucci-chain`](https://github.com/fialucci/fialucci-chain) | Eventually depends on this for `NN-external-oracle-crate.sh` integration coverage |

## Git workflow

PRs only against `main` (per the dev-workspace standing rule: every repo under `src/` except the dev-workspace superrepo is PR-only). `release.yml` publishes to crates.io on `vX.Y.Z` tag push, reading from the per-repo secret `FIALUCCI_ORACLE_CARGO_REGISTRY_TOKEN`.
