# Plutia Plutia is a deterministic, verifiable PLC mirror for `plc.directory` with signed checkpoints and a proof-serving API. ## Key Capabilities - Mirror and resolver modes. - Pebble-backed state/index storage. - Compressed append-only operation blocks (`zstd`) in mirror mode. - Deterministic canonical operation handling and signature-chain verification. - Signed Merkle checkpoints and DID inclusion proof API. - Corruption detection and restart-safe ingestion. - High-density storage scaling around ~1.2KB/op in benchmarked runs. - ~45x faster than naive replay in benchmarked runs. ## Trust Model - Plutia mirrors data from `https://plc.directory`. - Plutia validates operation signature chains and prev-link continuity according to configured verification policy. - Plutia does **not** alter PLC authority or introduce consensus. - Checkpoints are mirror commitments about what this mirror observed and verified, not global consensus. ## Modes - `mirror`: stores full verifiable operation history (`data/ops/*.zst`) + state + proofs/checkpoints. - `resolver`: stores resolved DID state/index only (no op block archive). ## Quick Start ```bash task build task verify:small task bench ``` ## Dev / Smoke Test (Docker Compose) ```bash VERSION="$(cat VERSION)" \ COMMIT="$(git rev-parse --short HEAD)" \ BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build ``` Compose build args are read from environment variables (`VERSION`, `COMMIT`, `BUILD_DATE`). Equivalent direct build: ```bash docker build \ --build-arg VERSION="$(cat VERSION)" \ --build-arg COMMIT="$(git rev-parse --short HEAD)" \ --build-arg BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ -t plutia:local . ``` In a second terminal, generate the mirror signing key inside the running container: ```bash docker compose -f docker-compose.yml -f docker-compose.dev.yml exec plutia /app/plutia keygen --out=/var/lib/plutia/mirror.key ``` Verify checkpoint and proof endpoints: ```bash curl -sS http://127.0.0.1:8080/checkpoints/latest | jq . DID="$(curl -sS 'http://127.0.0.1:8080/export?count=1' | head -n 1 | jq -r '.did')" curl -sS "http://127.0.0.1:8080/did/${DID}/proof" | jq . ``` ### CLI Commands ```bash plutia serve --config=config.default.yaml [--max-ops=0] plutia replay --config=config.default.yaml [--max-ops=0] plutia verify --config=config.default.yaml --did=did:plc:example plutia snapshot --config=config.default.yaml plutia bench --config=config.default.yaml --max-ops=200000 plutia compare --config=config.default.yaml --remote=https://mirror.example.com plutia keygen --out=./mirror.key [--force] plutia version ``` ## Versioning and Reproducible Builds Plutia follows semantic versioning, starting at `v0.1.0`. `plutia version` prints: - `Version` (defaults to `dev` if not injected) - `Commit` - `BuildDate` (UTC RFC3339) - `GoVersion` Build metadata is injected through ldflags: ```bash go build -trimpath \ -ldflags "-X main.version=v0.1.0 -X main.commit=$(git rev-parse --short HEAD) -X main.buildDate=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ -o ./bin/plutia ./cmd/plutia ``` Task runner equivalent: ```bash task build ``` ## HTTP API - `GET /health` - `GET /metrics` (Prometheus) - `GET /status` (includes build/version metadata) - `GET /did/{did}` - `GET /did/{did}/proof` - `GET /checkpoints/latest` - `GET /checkpoints/{sequence}` ## PLC API Compatibility Plutia includes read-only compatibility endpoints for `plc.directory` API consumers: - `GET /{did}` (returns `application/did+ld+json`) - `GET /{did}/log` - `GET /{did}/log/last` - `GET /{did}/log/audit` - `GET /{did}/data` - `GET /export` (NDJSON, `application/jsonlines`, supports `count` up to `1000`, and `after` RFC3339 filtering based on ingested operation timestamps) For audit/export compatibility fields, `createdAt` is sourced from the mirror's recorded ingest timestamp for each operation reference. Write behavior is intentionally unsupported: - `POST /{did}` returns `405 Method Not Allowed` with `Allow: GET` Verification features are additive extensions and remain available under: - `GET /did/{did}` - `GET /did/{did}/proof` - `GET /checkpoints/latest` - `GET /checkpoints/{sequence}` ## Metrics and Observability Prometheus series exposed at `/metrics` include: - `ingest_ops_total` - `ingest_ops_per_second` - `ingest_lag_ops` - `verify_failures_total` - `checkpoint_duration_seconds` - `checkpoint_sequence` - `disk_bytes_total` - `did_count` Operational hardening includes: - Per-IP token-bucket rate limits (stricter on proof endpoints). - Per-request timeout (default `10s`) with cancellation propagation. - Upstream ingestion retries with exponential backoff and `429` handling. - Graceful SIGINT/SIGTERM shutdown with flush-before-exit behavior. ## Running Your Own Mirror ### System Requirements - Go 1.25+ - SSD-backed storage recommended - RAM: 4GB minimum, 8GB+ recommended for larger throughput - CPU: multi-core recommended for parallel verification workers ### Disk Projections Using benchmarked density (~1.2KB/op total): - 5,000,000 ops: ~6GB - 10,000,000 ops: ~12GB Always keep extra headroom for compaction, checkpoints, and operational buffers. ### Example `config.default.yaml` See [`config.default.yaml`](./config.default.yaml). Core knobs: - `mode` - `verify` - `commit_batch_size` - `verify_workers` - `checkpoint_interval` - `rate_limit.*` - `request_timeout` ### Example `docker-compose.yml` ```yaml services: plutia: image: ghcr.io/fuwn/plutia:0.1.0 command: ["plutia", "serve", "--config=/etc/plutia/config.yaml"] ports: - "8080:8080" volumes: - ./config.default.yaml:/etc/plutia/config.yaml:ro - ./data:/var/lib/plutia restart: unless-stopped ``` ### Upgrade and Backup Guidance - Stop the process cleanly (`SIGTERM`) to flush pending writes. - Back up `data/index`, `data/ops`, and `data/checkpoints` together. - Keep the same `mode` per data directory across restarts. - Upgrade binaries first in staging, then production using the same on-disk data. ## Mirror Comparison Use: ```bash plutia compare --config=config.default.yaml --remote=https://mirror.example.com ``` The command fetches remote `/checkpoints/latest` and compares: - checkpoint sequence - DID Merkle root - signature presence Behavior: - same sequence + different root => divergence warning and non-zero exit - different sequences => reports which mirror is ahead and exits non-zero - matching sequence/root/signature presence => success ## License MIT OR Apache-2.0