Skip to content

Phase 2: fula-ingest node - verified byte ingress (client-side CID, quota-gated) #74

Description

@ehsan6sha

Phase 2 of the decentralization roadmap (uploads axis; cross-repo with functionland/fula-api).

Ships: docker/fula-ingest/ — a small Go service any provider/operator can run that accepts encrypted chunk BYTES so uploads no longer depend on a single gateway for the byte path:

  • PUT /v0/block?cid=<declared>: verifies blake3 raw-leaf CID over the body BEFORE storing (tampered byte => 422, hard rejection - the server-side half of tamper-evidence), then kubo block/put?cid-codec=raw&mhtype=blake3 and double-checks kubo's returned key.
  • S1 quota gate (never unmetered ingestion): mirrors the gateway exactly - GET {STORAGE_API_URL}/api/v1/storage with the client's Bearer JWT, short per-token cache; INGEST_QUOTA_MODE=strict|open (default mirrors gateway fail-open).
  • GET /health; size cap (INGEST_MAX_BLOCK_BYTES, default 4 MiB - chunks are 256 KiB + AEAD); bounded concurrency; retention = no GC (kubo default) until proof-of-possession exists (per plan).
  • Dockerfile + compose snippet; discovery/announce wiring (relay list) comes later - v0 clients receive the endpoint via config.

Verify (e2e on test infra): valid chunk accepted + retrievable from kubo by CID; tampered byte rejected 422 and NOT stored; over-quota/missing-JWT behavior per mode; works on ARM (fxblox additive container) and x86.

Scale invariant: per-chunk O(1), streaming, no per-item on-chain anything.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions