vole-document 0.1.0-alpha.13

Byte-exact procedural document storage: deterministic reconstruction state, typed residuals, and entropy-coded channels that materialize the exact original document bytes.
Documentation
name: vole-document

# All project commands run through these services. The host only invokes
# `docker compose run --rm <service> ...` (or the tools/*.sh wrappers).
#
#   docker compose run --rm dev  cargo test --all-features --locked
#   docker compose run --rm dev  cargo fmt --check
#   docker compose run --rm msrv cargo build --locked
#   docker compose run --rm tools qpdf --version
#
# The cargo registry and target directory are named volumes so builds are fast
# and identical across repeated gate runs; neither is part of the repository.
#
# ---------------------------------------------------------------------------
# RESOURCE / OOM CONTAINMENT (mandatory)
# ---------------------------------------------------------------------------
# Every service is hard-capped. This is not optional: the host's swap is
# **zram** (RAM-backed), so "swap" is not extra memory — an unbounded process
# can drive the whole machine into OOM. Each service therefore pins:
#
#   mem_limit     hard RSS cap; exceeding it OOM-kills the *container*, never
#                 the host.
#   memswap_limit equal to mem_limit, so the container cannot swap at all and
#                 cannot evade the cap. (memswap_limit must be >= mem_limit;
#                 setting them equal is the only way to disable swap.)
#   pids_limit    fork/thread bomb bound (a hostile input or a runaway build
#                 cannot exhaust the host's PID space).
#   cpus          optional CPU cap on the long-running/saturation-prone lanes.
#
# Raising a cap is a deliberate act: it must be justified by a measured need,
# recorded in the receipt, and never removed to make a failing run "pass".
# Prefer fixing the workload's memory behaviour over raising the ceiling.

services:
  # Rust 1.99.0 reference build + test gate. Heaviest lane (`--all-features`
  # compiles the ~68-crate `entropyfs` tree), so it keeps the largest cap.
  dev:
    build:
      context: .
      target: dev
    image: vole-document/dev:1.99.0
    # Host networking is required in restricted sandboxes where only the
    # pre-existing bridge/host networks have working DNS + egress.
    network_mode: host
    working_dir: /work
    mem_limit: 8g
    memswap_limit: 8g
    pids_limit: 4096
    environment:
      CARGO_TERM_COLOR: never
    volumes:
      - .:/work
      - cargo-registry:/usr/local/cargo/registry
      - cargo-target:/work/target
    command: ["cargo", "test", "--all-features", "--locked"]

  # MSRV 1.89.0 gate.
  msrv:
    build:
      context: .
      target: msrv
    image: vole-document/msrv:1.89.0
    network_mode: host
    working_dir: /work
    mem_limit: 6g
    memswap_limit: 6g
    pids_limit: 4096
    environment:
      CARGO_TERM_COLOR: never
      RUSTUP_TOOLCHAIN: "1.89.0"
    volumes:
      - .:/work
      - cargo-registry:/usr/local/cargo/registry
      - cargo-target-msrv:/work/target
    command: ["cargo", "build", "--locked"]

  # Small oracle-only lane (qpdf, Poppler, MuPDF, Ghostscript). No compiler.
  tools:
    build:
      context: .
      target: tools
    image: vole-document/tools:bookworm
    network_mode: host
    working_dir: /work
    mem_limit: 2g
    memswap_limit: 2g
    pids_limit: 1024
    volumes:
      - .:/work
    command: ["sh", "-c", "qpdf --version; pdfinfo -v; mutool -v; gs --version"]

  # Generator-family PDF corpus (Phase 7.0b). A separate, opt-in image carrying
  # real authoring generators (ReportLab, Cairo, LibreOffice, pdfTeX) plus qpdf
  # for --deterministic-id post-normalization. It is intentionally NOT part of
  # the fast `tools` gate; nothing else builds it. LibreOffice is memory-hungry,
  # hence the larger cap.
  producers:
    build:
      context: .
      target: producers
    image: vole-document/producers:bookworm
    network_mode: host
    working_dir: /work
    mem_limit: 8g
    memswap_limit: 8g
    pids_limit: 4096
    volumes:
      - .:/work
    command: ["sh", "-c", "python3 -c 'import reportlab; print(reportlab.Version)'; soffice --version | head -1; pdflatex --version | head -1; qpdf --version"]

  # Generic-compressor baseline ladder (Phase 7.0c). Runs in the pinned `dev`
  # toolchain (same base digest) plus gzip/zstd/xz/brotli/jq, so tools/baselines.sh
  # can invoke the real VOLE CLI and compare it against generic compressors on the
  # same complete files. Shares the dev cargo volumes so the built binary is
  # visible; it is opt-in and never part of a fast gate. Compressors at high
  # levels are memory-hungry, so this lane matches the largest cap and is CPU-capped.
  baseline:
    build:
      context: .
      target: baseline
    image: vole-document/baseline:1.99.0
    network_mode: host
    working_dir: /work
    mem_limit: 8g
    memswap_limit: 8g
    pids_limit: 4096
    cpus: 8
    environment:
      CARGO_TERM_COLOR: never
      VOLE_BIN: ./target/debug/vole-document
    volumes:
      - .:/work
      - cargo-registry:/usr/local/cargo/registry
      - cargo-target:/work/target
    command: ["sh", "-c", "gzip --version | head -1; zstd --version; xz --version | head -1; brotli --version; jq --version"]

  # Phase-11.9 fair-baseline court. Same pinned base digest as `baseline`/`dev`
  # (derived FROM baseline), plus sqlite3 + Poppler + qpdf so one image runs
  # `tools/field-court.sh` end-to-end: the VOLE field CLI, the A0 raw PDF tooling
  # lane, the A1 preprocessed-SQLite lane, and the generic compressors.
  db-baseline:
    build:
      context: .
      target: db-baseline
    image: vole-document/db-baseline:1.99.0
    network_mode: host
    working_dir: /work
    mem_limit: 6g
    memswap_limit: 6g
    pids_limit: 4096
    cpus: 8
    environment:
      CARGO_TERM_COLOR: never
      VOLE_BIN: ./target/debug/vole-document
    volumes:
      - .:/work
      - cargo-registry:/usr/local/cargo/registry
      - cargo-target:/work/target
    command: ["sh", "-c", "sqlite3 --version; pdftotext -v; qpdf --version | head -1"]

  # Phase-11.13 LLM working-set *token* court (opt-in; never part of a fast
  # gate). Same pinned base digest as `dev`/`baseline`, plus Poppler (`pdftotext`
  # for B0/B1), `jq`, and a pinned HuggingFace `tokenizers` runtime. Runs
  # `tools/llm-token-court.sh`; the vendored tokenizer asset is on disk, so the
  # court is offline-deterministic. Capped like the other measurement lanes.
  llm-workingset:
    build:
      context: .
      target: llm-workingset
    image: vole-document/llm-workingset:1.99.0
    network_mode: host
    working_dir: /work
    mem_limit: 4g
    memswap_limit: 4g
    pids_limit: 2048
    cpus: 4
    environment:
      CARGO_TERM_COLOR: never
      VOLE_BIN: ./target/debug/vole-document
    volumes:
      - .:/work
      - cargo-registry:/usr/local/cargo/registry
      - cargo-target:/work/target
    command: ["sh", "-c", "python3 -c 'import tokenizers; print(tokenizers.__version__)'"]

  # Dependency-policy gate (cargo-audit + cargo-deny).
  policy:
    build:
      context: .
      target: policy
    image: vole-document/policy:1.99.0
    network_mode: host
    working_dir: /work
    mem_limit: 4g
    memswap_limit: 4g
    pids_limit: 2048
    volumes:
      - .:/work
      - cargo-registry:/usr/local/cargo/registry
    command: ["cargo", "deny", "check"]

  # Coverage-guided fuzzing (Phase 7). A pinned dated nightly + cargo-fuzz.
  # No default command: tools/fuzz.sh drives `cargo fuzz run` explicitly.
  # The /work/fuzz/target volume keeps fuzz builds fast and out of the repo.
  # Fuzzing is the classic runaway-memory lane, so it is tightly capped and
  # CPU-limited; a finding is a crash/resource kill *inside* this container.
  fuzz:
    build:
      context: .
      target: fuzz
    image: vole-document/fuzz:nightly-2026-10-04
    network_mode: host
    working_dir: /work
    mem_limit: 4g
    memswap_limit: 4g
    pids_limit: 2048
    cpus: 4
    environment:
      CARGO_TERM_COLOR: never
      RUSTUP_TOOLCHAIN: "nightly-2026-10-04"
    volumes:
      - .:/work
      - cargo-registry:/usr/local/cargo/registry
      - cargo-target-fuzz:/work/fuzz/target

volumes:
  cargo-registry:
  cargo-target:
  cargo-target-msrv:
  cargo-target-fuzz: