From 519a78af5b6de36b87bc4e2bbdaf0833e30fc3b9 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 14 Oct 2026 00:43:46 +0000 Subject: [PATCH 1/4] =?UTF-8?q?ci:=20estate=20workflow=20set=20=E2=80=94?= =?UTF-8?q?=20current=20standards=20pins,=20thin=20CodeQL,=20closed=20acti?= =?UTF-8?q?ons.lock?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Brings januskey's CI in line with the standards CI regularisation spec (docs/superpowers/specs/2026-09-02-cicd-regularisation-design.adoc) and fixes the checks that were red or never started on main. Re-pin (standards main 7d2041a, 2026-10-13): governance, hypatia-scan, mirror, rust-ci, scorecard, secret-scanner. The old pin (8435558) carried the Allowlist Preflight that called the live Actions API without a token (the GH_TOKEN failure on every run); the current bundle splits that into a tokenless preflight plus a credentialed advisory, which gets HYPATIA_SCAN_PAT and passes with a notice when it is unset. The current mirror reusable runs a forge only when vars._MIRROR_ENABLED is 'true', so the four forges with no key stop failing. CodeQL: thin caller of codeql-reusable (actions + rust). The inline workflow named codeql-action v4.38.2 while the lock held v4.38.0, so every run ended in startup_failure; the reusable pins the estate's v4.38.0 commit. Retired, per the spec: workflow-linter.yml (FOLD into governance, whose "Workflow security linter" already runs), cargo-audit.yml (fold into rust-ci via enable_audit, with a weekly schedule kept), instant-sync.yml (DROP; disabled), pages.yml (the disabled Ddraig duplicate of casket-pages.yml, which stays the one Pages deployer), push-email-notify.yml (disabled, and its action had drifted from the lock). main-estate-audit.yml: SPDX header and top-level permissions (the governance linter's only finding on main), timeout, and the PERIODIC tier the signal discipline gives it: push to main, weekly, dispatch; never pull_request. dogfood-gate.yml: the DEED validator now skips audits/assail-classifications.a2ml, which is panic-attack's suppression registry, not a repo manifest. squabble.yml (new): hyperpolymath/cicd-squabbler's action pinned by commit. `squabble chains` on push, PR and weekly, failing only on a blocking finding (cycle or dead upstream); `squabble fight` on dispatch for a PR, propose-only, with exit 3 (no required_status_checks rule) reported as a notice. actions.lock: rebuilt transitively closed. Every workflow ref and every nested ref has its own record; the standards reusables are listed as hyperpolymath/standards@7d2041a with their transitive actions. Drift fixed: haskell-actions/setup v2.12.1 (casket-pages startup_failure), codeql. The Agda and Idris workflows use the locked actions/checkout@v7.0.1 tag like every other workflow here. UUID literals in two p2p tests, the attestation benchmark and KEY_LIFECYCLE.adoc are now version 8, for the governance bundle's UUID job (ADR-008, strict). They are opaque fixture strings; nothing parses their version. Verified locally: standards check-actions-lock-gate.sh (valid; one advisory, sha-as-ref on the squabbler pin its README requires), check-lock-sync.sh (in sync), every governance-reusable job at 7d2041a run against this tree, the locked DEED validator, actionlint. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Kjdf4Mf4Z41bNQh2TrsEp9 --- .github/workflows/actions.lock | 170 +++++++++++++----------- .github/workflows/agda-proofs.yml | 2 +- .github/workflows/cargo-audit.yml | 62 --------- .github/workflows/codeql.yml | 59 +++----- .github/workflows/dogfood-gate.yml | 14 ++ .github/workflows/governance.yml | 6 +- .github/workflows/hypatia-scan.yml | 2 +- .github/workflows/idris-abi.yml | 2 +- .github/workflows/instant-sync.yml | 40 ------ .github/workflows/main-estate-audit.yml | 14 +- .github/workflows/mirror.yml | 2 +- .github/workflows/pages.yml | 57 -------- .github/workflows/push-email-notify.yml | 36 ----- .github/workflows/rust-ci.yml | 10 +- .github/workflows/scorecard.yml | 2 +- .github/workflows/secret-scanner.yml | 2 +- .github/workflows/squabble.yml | 107 +++++++++++++++ .github/workflows/workflow-linter.yml | 57 -------- benches/januskey_benchmarks.rs | 2 +- crates/januskey-cli/tests/p2p_test.rs | 2 +- docs/security/KEY_LIFECYCLE.adoc | 6 +- tests/p2p/component_p2p_test.rs | 2 +- 22 files changed, 270 insertions(+), 386 deletions(-) delete mode 100644 .github/workflows/cargo-audit.yml delete mode 100644 .github/workflows/instant-sync.yml delete mode 100644 .github/workflows/pages.yml delete mode 100644 .github/workflows/push-email-notify.yml create mode 100644 .github/workflows/squabble.yml delete mode 100644 .github/workflows/workflow-linter.yml diff --git a/.github/workflows/actions.lock b/.github/workflows/actions.lock index 46ad1a7..bc7938f 100644 --- a/.github/workflows/actions.lock +++ b/.github/workflows/actions.lock @@ -3,9 +3,9 @@ # Docs: https://gh.io/actions-lockfile version: 'v0.0.2' workflows: - '.github/workflows/boj-build.yml': + '.github/workflows/agda-proofs.yml': - 'actions/checkout@v7.0.1' - '.github/workflows/cargo-audit.yml': + '.github/workflows/boj-build.yml': - 'actions/checkout@v7.0.1' '.github/workflows/casket-pages.yml': - 'actions/cache@v6.1.0' @@ -13,10 +13,9 @@ workflows: - 'actions/configure-pages@v6.0.0' - 'actions/deploy-pages@v5.0.1' - 'actions/upload-pages-artifact@v5.0.0' - - 'haskell-actions/setup@v2.12.0' + - 'haskell-actions/setup@v2.12.1' '.github/workflows/codeql.yml': - - 'actions/checkout@v7.0.1' - - 'github/codeql-action@v4.38.0' + - 'hyperpolymath/standards@7d2041a2315572d8257341449257e6cfddc9aebc' '.github/workflows/dashboard-check.yml': - 'actions/checkout@v7.0.1' - 'dtolnay/rust-toolchain@v1' @@ -30,33 +29,44 @@ workflows: - 'actions/checkout@v7.0.1' - 'dtolnay/rust-toolchain@v1' - 'swatinem/rust-cache@v2.9.2' - '.github/workflows/governance.yml': [] - '.github/workflows/hypatia-scan.yml': [] - '.github/workflows/instant-sync.yml': - - 'peter-evans/repository-dispatch@v4.0.1' + '.github/workflows/governance.yml': + - 'hyperpolymath/standards@7d2041a2315572d8257341449257e6cfddc9aebc' + '.github/workflows/hypatia-scan.yml': + - 'hyperpolymath/standards@7d2041a2315572d8257341449257e6cfddc9aebc' + '.github/workflows/idris-abi.yml': + - 'actions/checkout@v7.0.1' '.github/workflows/label-triage.yml': [] '.github/workflows/labels.yml': [] '.github/workflows/main-estate-audit.yml': - 'actions/checkout@v7.0.1' - 'hyperpolymath/cicd-suite@main' - '.github/workflows/mirror.yml': [] - '.github/workflows/pages.yml': - - 'actions/checkout@v7.0.1' - - 'actions/deploy-pages@v5.0.1' - - 'actions/upload-pages-artifact@v5.0.0' - '.github/workflows/push-email-notify.yml': - - 'dawidd6/action-send-mail@v3.12.0' - '.github/workflows/rust-ci.yml': [] - '.github/workflows/scorecard.yml': [] - '.github/workflows/secret-scanner.yml': [] - '.github/workflows/workflow-linter.yml': + '.github/workflows/mirror.yml': + - 'hyperpolymath/standards@7d2041a2315572d8257341449257e6cfddc9aebc' + '.github/workflows/rust-ci.yml': + - 'hyperpolymath/standards@7d2041a2315572d8257341449257e6cfddc9aebc' + '.github/workflows/scorecard.yml': + - 'hyperpolymath/standards@7d2041a2315572d8257341449257e6cfddc9aebc' + '.github/workflows/secret-scanner.yml': + - 'hyperpolymath/standards@7d2041a2315572d8257341449257e6cfddc9aebc' + '.github/workflows/squabble.yml': - 'actions/checkout@v7.0.1' + - 'hyperpolymath/cicd-squabbler@b34cd53f9157d1b8a4f9c5c8d843846557f8622c' dependencies: + 'actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9': + ref: 'v6.1.0' + commit: 'sha1-55cc8345863c7cc4c66a329aec7e433d2d1c52a9' + owner_id: 44036562 + repo_id: 215566462 'actions/cache@v6.1.0': ref: 'v6.1.0' commit: 'sha1-55cc8345863c7cc4c66a329aec7e433d2d1c52a9' owner_id: 44036562 repo_id: 215566462 + 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1': + ref: 'v7.0.1' + commit: 'sha1-3d3c42e5aac5ba805825da76410c181273ba90b1' + owner_id: 44036562 + repo_id: 197814629 'actions/checkout@v7.0.1': ref: 'v7.0.1' commit: 'sha1-3d3c42e5aac5ba805825da76410c181273ba90b1' @@ -77,6 +87,11 @@ dependencies: commit: 'sha1-bbbca2ddaa5d8feaa63e36b76fdaad77386f024f' owner_id: 44036562 repo_id: 192625955 + 'actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9': + ref: 'cf430e030ddbb5b0abf93d22962f4752f3646cd9' + commit: 'sha1-cf430e030ddbb5b0abf93d22962f4752f3646cd9' + owner_id: 44036562 + repo_id: 192625955 'actions/upload-pages-artifact@v5.0.0': ref: 'v5.0.0' commit: 'sha1-fc324d3547104276b827a68afc52ff2a11cc49c9' @@ -84,31 +99,51 @@ dependencies: repo_id: 496012378 uses: - 'actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f' - 'dawidd6/action-send-mail@v3.12.0': - ref: 'v3.12.0' - commit: 'sha1-2cea9617b09d79a095af21254fbcb7ae95903dde' - owner_id: 9713907 - repo_id: 222439721 'dependabot/fetch-metadata@v3.1.0': ref: 'v3.1.0' commit: 'sha1-25dd0e34f4fe68f24cc83900b1fe3fe149efef98' owner_id: 27347476 repo_id: 371068214 + 'dtolnay/rust-toolchain@e2a55d2ffb04f378e9626c28d38b36d230d1e12f': + ref: 'e2a55d2ffb04f378e9626c28d38b36d230d1e12f' + commit: 'sha1-e2a55d2ffb04f378e9626c28d38b36d230d1e12f' + owner_id: 1940490 + repo_id: 260749683 'dtolnay/rust-toolchain@v1': ref: 'v1' commit: 'sha1-02cb101ec7c40f2c49e1d9714d64511d8e1b74de' owner_id: 1940490 repo_id: 260749683 - 'github/codeql-action@v4.38.0': - ref: 'v4.38.0' + 'editorconfig-checker/action-editorconfig-checker@51f63319f592f97930c73d9c46184d20bd206393': + ref: '51f63319f592f97930c73d9c46184d20bd206393' + commit: 'sha1-51f63319f592f97930c73d9c46184d20bd206393' + owner_id: 26415196 + repo_id: 297874902 + 'erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124': + ref: 'v1.24.1' + commit: 'sha1-54075bcc5e249e4758d363f27d099f55d843f124' + owner_id: 47606891 + repo_id: 331103973 + 'github/codeql-action@b96794f015dfd88f77b49b1c93e0fa7110f94c63': + ref: 'b96794f015dfd88f77b49b1c93e0fa7110f94c63' commit: 'sha1-b96794f015dfd88f77b49b1c93e0fa7110f94c63' owner_id: 9919 repo_id: 259445878 - 'haskell-actions/setup@v2.12.0': - ref: 'v2.12.0' - commit: 'sha1-6037f33647c3f17758a2356c80fc4a53d7e0685d' + 'goto-bus-stop/setup-zig@abea47f85e598557f500fa1fd2ab7464fcb39406': + ref: 'abea47f85e598557f500fa1fd2ab7464fcb39406' + commit: 'sha1-abea47f85e598557f500fa1fd2ab7464fcb39406' + owner_id: 1006268 + repo_id: 212984112 + 'haskell-actions/setup@v2.12.1': + ref: 'v2.12.1' + commit: 'sha1-0f8e8c99d88aeb3fbfd523f1ef2c6f762d10d64d' owner_id: 75048950 repo_id: 623796603 + 'hyperpolymath/cicd-squabbler@b34cd53f9157d1b8a4f9c5c8d843846557f8622c': + ref: 'b34cd53f9157d1b8a4f9c5c8d843846557f8622c' + commit: 'sha1-b34cd53f9157d1b8a4f9c5c8d843846557f8622c' + owner_id: 6759885 + repo_id: 1284026967 'hyperpolymath/cicd-suite@main': ref: 'main' commit: 'sha1-5a10b72e574ef63855fafd4568a4c178657f3294' @@ -137,61 +172,38 @@ dependencies: commit: 'sha1-c1a34884054fabf0e9de81dbf68f4ba7874e85f1' owner_id: 6759885 repo_id: 1275650185 - 'peter-evans/repository-dispatch@v4.0.1': - ref: 'v4.0.1' - commit: 'sha1-28959ce8df70de7be546dd1250a005dd32156697' - owner_id: 18365890 - repo_id: 220359305 - 'swatinem/rust-cache@v2.9.2': - ref: 'v2.9.2' + 'hyperpolymath/standards@7d2041a2315572d8257341449257e6cfddc9aebc': + ref: '7d2041a2315572d8257341449257e6cfddc9aebc' + commit: 'sha1-7d2041a2315572d8257341449257e6cfddc9aebc' + owner_id: 6759885 + repo_id: 1116521501 + uses: + - 'actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9' + - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' + - 'actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9' + - 'dtolnay/rust-toolchain@e2a55d2ffb04f378e9626c28d38b36d230d1e12f' + - 'editorconfig-checker/action-editorconfig-checker@51f63319f592f97930c73d9c46184d20bd206393' + - 'erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124' + - 'github/codeql-action@b96794f015dfd88f77b49b1c93e0fa7110f94c63' + - 'goto-bus-stop/setup-zig@abea47f85e598557f500fa1fd2ab7464fcb39406' + - 'ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc' + - 'swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6' + - 'webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555' + 'ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc': + ref: 'v2.4.4' + commit: 'sha1-2d1146689b8cda280b9bc96326124645441f03bc' + owner_id: 67707773 + repo_id: 421101922 + 'Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6': + ref: 'v2' commit: 'sha1-6323deb102c322ba6fcbdcafc7e3dddab59af2b6' owner_id: 580492 repo_id: 298565987 - 'Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6': - ref: 'v2' + 'swatinem/rust-cache@v2.9.2': + ref: 'v2.9.2' commit: 'sha1-6323deb102c322ba6fcbdcafc7e3dddab59af2b6' owner_id: 580492 repo_id: 298565987 - 'actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9': - ref: 'v6.1.0' - commit: 'sha1-55cc8345863c7cc4c66a329aec7e433d2d1c52a9' - owner_id: 44036562 - repo_id: 215566462 - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1': - ref: 'v7.0.1' - commit: 'sha1-3d3c42e5aac5ba805825da76410c181273ba90b1' - owner_id: 44036562 - repo_id: 197814629 - 'actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a': - ref: 'v7.0.1' - commit: 'sha1-043fb46d1a93c77aae656e7c1c64a875d1fc6a0a' - owner_id: 44036562 - repo_id: 192625955 - 'denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed': - ref: 'v2.0.5' - commit: 'sha1-22d081ff2d3a40755e97629de92e3bcbfa7cf2ed' - owner_id: 42048915 - repo_id: 356423100 - 'dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772': - ref: 'stable' - commit: 'sha1-6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772' - owner_id: 1940490 - repo_id: 260749683 - 'editorconfig-checker/action-editorconfig-checker@840e866d93b8e032123c23bac69dece044d4d84c': - ref: 'v2.2.0' - commit: 'sha1-840e866d93b8e032123c23bac69dece044d4d84c' - owner_id: 26415196 - repo_id: 297874902 - 'erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124': - ref: 'v1.24.1' - commit: 'sha1-54075bcc5e249e4758d363f27d099f55d843f124' - owner_id: 47606891 - repo_id: 331103973 - 'ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc': - ref: 'v2.4.4' - commit: 'sha1-2d1146689b8cda280b9bc96326124645441f03bc' - owner_id: 67707773 - repo_id: 421101922 'webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555': ref: 'v0.10.0' commit: 'sha1-e83874834305fe9a4a2997156cb26c5de65a8555' diff --git a/.github/workflows/agda-proofs.yml b/.github/workflows/agda-proofs.yml index 4399a3c..7c7e653 100644 --- a/.github/workflows/agda-proofs.yml +++ b/.github/workflows/agda-proofs.yml @@ -24,7 +24,7 @@ jobs: apt-get update apt-get install --no-install-recommends -y ca-certificates git agda-bin=2.6.4.3-1+b2 - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@v7.0.1 with: persist-credentials: false - name: Verify the checked Agda version diff --git a/.github/workflows/cargo-audit.yml b/.github/workflows/cargo-audit.yml deleted file mode 100644 index e03918f..0000000 --- a/.github/workflows/cargo-audit.yml +++ /dev/null @@ -1,62 +0,0 @@ -# This workflow is managed by gh actions-lock. -# SPDX-License-Identifier: MPL-2.0 -# This workflow is managed by gh actions-lock. -# Prevention workflow - audits Rust dependencies for vulnerabilities -name: Cargo Audit - -on: - push: - branches: [main] - paths: - - '**/Cargo.toml' - - '**/Cargo.lock' - pull_request: - paths: - - '**/Cargo.toml' - - '**/Cargo.lock' - schedule: - - cron: '0 6 * * 1' # Weekly on Monday - -permissions: read-all - -jobs: - audit: - runs-on: ubuntu-latest - timeout-minutes: 15 - steps: - - uses: actions/checkout@v7.0.1 - - - name: Install cargo-audit - run: cargo install cargo-audit --locked - - - name: Run cargo audit - run: cargo audit --deny warnings - - - name: Check for unmaintained crates - run: cargo audit --deny unmaintained - - # Optional: Create issues for vulnerabilities - create-issue: - runs-on: ubuntu-latest - timeout-minutes: 15 - needs: audit - # Only file a tracking issue on push/schedule runs. On pull_request events - # GITHUB_TOKEN is forced read-only, so `gh issue create` 403s; guarding here - # keeps PR runs green (the audit job itself still gates the PR). - if: failure() && github.event_name != 'pull_request' - permissions: - issues: write - steps: - - uses: actions/checkout@v7.0.1 - - - name: Create vulnerability issue - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - EXISTING=$(gh issue list --label "security,dependencies" --state open --json number -q '.[0].number') - if [ -z "$EXISTING" ]; then - gh issue create \ - --title "Security: Dependency vulnerabilities detected" \ - --body "cargo audit found vulnerabilities. Run \`cargo audit\` locally for details." \ - --label "security,dependencies" - fi diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 2ec9575..383114c 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,6 +1,11 @@ # This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 -# This workflow is managed by gh actions-lock. +# CodeQL — thin caller of the estate reusable in hyperpolymath/standards +# (CI regularisation spec: "REMAKE · GATE: thin caller of codeql-reusable.yml"). +# One call per language, per the reusable's design. januskey has no +# JavaScript/TypeScript, so the reusable's javascript-typescript default would +# fail with "no source code seen"; it analyses the workflow YAML (actions) and +# the Rust crates (rust, build-mode none). name: CodeQL Security Analysis on: @@ -11,48 +16,26 @@ on: schedule: - cron: '0 6 1 * *' # monthly 1st 06:00 UTC -# Estate guardrail: cancel superseded runs so re-pushes / rebased PR -# updates do not pile up queued runs against the shared account-wide -# Actions concurrency pool. Applied only to read-only check workflows -# (no publish/mutation), so cancelling a superseded run is always safe. +# Queue rather than cancel: a cancelled CodeQL run uploads nothing, and a +# ruleset that expects a code-scanning result for the head commit then waits +# on it indefinitely (same reasoning as standards' own codeql.yml). concurrency: group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true + cancel-in-progress: false +# Must cover everything the reusable's analyze job requests: a reusable cannot +# widen its caller's grant, and asking for more ends the run in startup_failure. permissions: actions: read contents: read + security-events: write jobs: - analyze: - runs-on: ubuntu-latest - timeout-minutes: 15 - permissions: - contents: read - security-events: write - strategy: - fail-fast: false - matrix: - include: - # januskey has no JavaScript/TypeScript source — the - # javascript-typescript extractor errors with "no source code seen" - # (exit 32, configuration error). CodeQL's GitHub Actions analysis - # scans the workflow YAML that does exist here, keeping this a - # meaningful security check (same fix as oblibeny + maa-framework). - - language: actions - build-mode: none - - steps: - - name: Checkout - uses: actions/checkout@v7.0.1 - - - name: Initialize CodeQL - uses: github/codeql-action/init@v4.38.2 - with: - languages: ${{ matrix.language }} - build-mode: ${{ matrix.build-mode }} - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v4.38.2 - with: - category: "/language:${{ matrix.language }}" + codeql-actions: + uses: hyperpolymath/standards/.github/workflows/codeql-reusable.yml@7d2041a2315572d8257341449257e6cfddc9aebc # standards main, 2026-10-13 + with: + language: actions + codeql-rust: + uses: hyperpolymath/standards/.github/workflows/codeql-reusable.yml@7d2041a2315572d8257341449257e6cfddc9aebc # standards main, 2026-10-13 + with: + language: rust diff --git a/.github/workflows/dogfood-gate.yml b/.github/workflows/dogfood-gate.yml index c11235b..455922a 100644 --- a/.github/workflows/dogfood-gate.yml +++ b/.github/workflows/dogfood-gate.yml @@ -46,6 +46,20 @@ jobs: with: path: '.' strict: 'false' + # The action's defaults, plus audits/assail-classifications.a2ml: that + # file is not a repo manifest but panic-attack's suppression registry + # (read from /audits/assail-classifications.a2ml), so the + # manifest identity check does not apply to it (hypatia#243 pattern: + # tell a target file from a tool-data file). + paths-ignore: | + vendor/ + vendored/ + verified-container-spec/ + .audittraining/ + integration/fixtures/ + test/fixtures/ + tests/fixtures/ + audits/assail-classifications.a2ml - name: Write summary run: | diff --git a/.github/workflows/governance.yml b/.github/workflows/governance.yml index de59b20..5be3b35 100644 --- a/.github/workflows/governance.yml +++ b/.github/workflows/governance.yml @@ -34,8 +34,12 @@ permissions: jobs: governance: - uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@84355587cb2a1f86e6882de83514a32db2646e7a + uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@7d2041a2315572d8257341449257e6cfddc9aebc # standards main, 2026-10-13 permissions: actions: read contents: read security-events: write + # Only the one secret the reusable declares; with it unset, the credentialed + # "Live Actions policy" job records a notice and passes. + secrets: + HYPATIA_SCAN_PAT: ${{ secrets.HYPATIA_SCAN_PAT }} diff --git a/.github/workflows/hypatia-scan.yml b/.github/workflows/hypatia-scan.yml index 205a9b2..0565597 100644 --- a/.github/workflows/hypatia-scan.yml +++ b/.github/workflows/hypatia-scan.yml @@ -28,5 +28,5 @@ permissions: jobs: hypatia: - uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@84355587cb2a1f86e6882de83514a32db2646e7a + uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@7d2041a2315572d8257341449257e6cfddc9aebc # standards main, 2026-10-13 secrets: inherit diff --git a/.github/workflows/idris-abi.yml b/.github/workflows/idris-abi.yml index 617cfd3..a5e69e4 100644 --- a/.github/workflows/idris-abi.yml +++ b/.github/workflows/idris-abi.yml @@ -22,7 +22,7 @@ jobs: image: ghcr.io/stefan-hoeck/idris2-pack@sha256:f0758996a931fb35d9ecb1de273c4d59dabe2a09b433afc7e357f65a08b7e1ff steps: - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@v7.0.1 with: persist-credentials: false - name: ABI tree invariants diff --git a/.github/workflows/instant-sync.yml b/.github/workflows/instant-sync.yml deleted file mode 100644 index 74669b7..0000000 --- a/.github/workflows/instant-sync.yml +++ /dev/null @@ -1,40 +0,0 @@ -# This workflow is managed by gh actions-lock. -# SPDX-License-Identifier: MPL-2.0 -# This workflow is managed by gh actions-lock. -# Instant Forge Sync - Triggers propagation to all forges on push/release -name: Instant Sync - -on: - push: - branches: [main, master] - release: - types: [published] - -permissions: - actions: read - contents: read - -jobs: - dispatch: - runs-on: ubuntu-latest - timeout-minutes: 15 - steps: - - name: Trigger Propagation - # Without the secret this step fails on every push (gitbot-fleet#433). - # Skipping is honest; a red tick on a misconfigured repo is not a signal. - if: ${{ secrets.FARM_DISPATCH_TOKEN != '' }} - uses: peter-evans/repository-dispatch@v4.0.1 - with: - token: ${{ secrets.FARM_DISPATCH_TOKEN }} - repository: hyperpolymath/.git-private-farm - event-type: propagate - client-payload: |- - { - "repo": "${{ github.event.repository.name }}", - "ref": "${{ github.ref }}", - "sha": "${{ github.sha }}", - "forges": "" - } - - - name: Confirm - run: echo "::notice::Propagation triggered for ${{ github.event.repository.name }}" diff --git a/.github/workflows/main-estate-audit.yml b/.github/workflows/main-estate-audit.yml index e249814..6b98a83 100644 --- a/.github/workflows/main-estate-audit.yml +++ b/.github/workflows/main-estate-audit.yml @@ -1,16 +1,24 @@ # This workflow is managed by gh actions-lock. - +# SPDX-License-Identifier: MPL-2.0 +# Central estate audit (hyperpolymath/cicd-suite). PERIODIC tier under the +# standards CI signal discipline: push to main, weekly, or on demand; never on +# pull_request, and never a required check (standards config/rulesets/gates.json). name: Central Estate CI/CD Audit on: push: branches: [ "main" ] - pull_request: - branches: [ "main" ] + schedule: + - cron: '41 4 * * 1' # weekly, Monday 04:41 UTC + workflow_dispatch: + +permissions: + contents: read jobs: estate-audit: runs-on: ubuntu-latest + timeout-minutes: 20 steps: - uses: actions/checkout@v7.0.1 diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml index 71c497b..671da67 100644 --- a/.github/workflows/mirror.yml +++ b/.github/workflows/mirror.yml @@ -14,7 +14,7 @@ permissions: jobs: mirror: - uses: hyperpolymath/standards/.github/workflows/mirror-reusable.yml@84355587cb2a1f86e6882de83514a32db2646e7a + uses: hyperpolymath/standards/.github/workflows/mirror-reusable.yml@7d2041a2315572d8257341449257e6cfddc9aebc # standards main, 2026-10-13 secrets: inherit permissions: actions: read diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml deleted file mode 100644 index 6a0d6ca..0000000 --- a/.github/workflows/pages.yml +++ /dev/null @@ -1,57 +0,0 @@ -# This workflow is managed by gh actions-lock. -# SPDX-License-Identifier: MPL-2.0 -# This workflow is managed by gh actions-lock. -name: GitHub Pages (Ddraig SSG) -on: - push: - branches: [main, master] - workflow_dispatch: -permissions: - actions: read - contents: read - pages: write - id-token: write -concurrency: - group: "pages" - cancel-in-progress: false -jobs: - build: - runs-on: ubuntu-latest - timeout-minutes: 15 - container: - image: ghcr.io/stefan-hoeck/idris2-pack@sha256:f0758996a931fb35d9ecb1de273c4d59dabe2a09b433afc7e357f65a08b7e1ff - steps: - - name: Checkout Site - uses: actions/checkout@v7.0.1 - - name: Checkout Ddraig SSG - uses: actions/checkout@v7.0.1 - with: - repository: hyperpolymath/ddraig-ssg - path: .ddraig-ssg - - name: Compile Ddraig - working-directory: .ddraig-ssg - run: idris2 Ddraig.idr -o ddraig - - name: Build site - run: | - mkdir -p src - if [ ! -f src/index.md ] && [ -f README.md ]; then - cp README.md src/index.md - elif [ ! -f src/index.md ]; then - echo "# ${GITHUB_REPOSITORY}" > src/index.md - fi - ./.ddraig-ssg/build/exec/ddraig build src _site https://hyperpolymath.github.io/${GITHUB_REPOSITORY#*/} - - name: Upload artifact - uses: actions/upload-pages-artifact@v5.0.0 - with: - path: '_site' - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - timeout-minutes: 15 - needs: build - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v5.0.1 diff --git a/.github/workflows/push-email-notify.yml b/.github/workflows/push-email-notify.yml deleted file mode 100644 index 4f70d07..0000000 --- a/.github/workflows/push-email-notify.yml +++ /dev/null @@ -1,36 +0,0 @@ -# This workflow is managed by gh actions-lock. -# SPDX-License-Identifier: MPL-2.0 -# This workflow is managed by gh actions-lock. -# Dormant push-email notification. ARMED by setting the repo variable -# PUSH_EMAIL_ENABLED=true (the single on/off switch). Addresses are pre-filled; -# sending needs the org SMTP secrets (SMTP_HOST/PORT/USER/PASS). Inherited by -# new repos from the template; placed on existing repos by the farm sweep. -name: Push email notification -on: - push: {} -permissions: - actions: read - contents: read -jobs: - notify: - name: Email on push - if: ${{ vars.PUSH_EMAIL_ENABLED == 'true' }} - runs-on: ubuntu-latest - steps: - - name: Send push notification email - uses: dawidd6/action-send-mail@v22 - with: - server_address: ${{ secrets.SMTP_HOST }} - server_port: ${{ secrets.SMTP_PORT }} - secure: true - username: ${{ secrets.SMTP_USER }} - password: ${{ secrets.SMTP_PASS }} - from: "GitHub Push <${{ secrets.SMTP_USER }}>" - to: "jonathan.jewell@gmail.com j.d.a.jewell@open.ac.uk" - subject: "[${{ github.repository }}] push to ${{ github.ref_name }} by ${{ github.actor }}" - body: | - Repository: ${{ github.repository }} - Branch: ${{ github.ref_name }} - Pusher: ${{ github.actor }} - Compare: ${{ github.event.compare }} - Head msg: ${{ github.event.head_commit.message }} diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index d7dafc6..7e3c760 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -10,6 +10,10 @@ on: push: branches: [main, master] pull_request: + # Weekly, so a new RustSec advisory against an unchanged Cargo.lock is still + # caught (the scheduled cargo-audit.yml was folded into this workflow). + schedule: + - cron: '0 6 * * 1' permissions: actions: read @@ -17,8 +21,12 @@ permissions: jobs: rust-ci: - uses: hyperpolymath/standards/.github/workflows/rust-ci-reusable.yml@84355587cb2a1f86e6882de83514a32db2646e7a + uses: hyperpolymath/standards/.github/workflows/rust-ci-reusable.yml@7d2041a2315572d8257341449257e6cfddc9aebc # standards main, 2026-10-13 permissions: actions: read contents: read security-events: write + with: + # Replaces the retired cargo-audit.yml (standards CI regularisation spec: + # fold the audit into rust-ci through enable_audit). + enable_audit: true diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 235c344..4d742e4 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -17,5 +17,5 @@ jobs: contents: read security-events: write id-token: write - uses: hyperpolymath/standards/.github/workflows/scorecard-reusable.yml@84355587cb2a1f86e6882de83514a32db2646e7a + uses: hyperpolymath/standards/.github/workflows/scorecard-reusable.yml@7d2041a2315572d8257341449257e6cfddc9aebc # standards main, 2026-10-13 secrets: inherit diff --git a/.github/workflows/secret-scanner.yml b/.github/workflows/secret-scanner.yml index 1aad290..f3b5967 100644 --- a/.github/workflows/secret-scanner.yml +++ b/.github/workflows/secret-scanner.yml @@ -22,4 +22,4 @@ jobs: actions: read security-events: write contents: read - uses: hyperpolymath/standards/.github/workflows/secret-scanner-reusable.yml@84355587cb2a1f86e6882de83514a32db2646e7a + uses: hyperpolymath/standards/.github/workflows/secret-scanner-reusable.yml@7d2041a2315572d8257341449257e6cfddc9aebc # standards main, 2026-10-13 diff --git a/.github/workflows/squabble.yml b/.github/workflows/squabble.yml new file mode 100644 index 0000000..ffb5426 --- /dev/null +++ b/.github/workflows/squabble.yml @@ -0,0 +1,107 @@ +# This workflow is managed by gh actions-lock. +# SPDX-License-Identifier: MPL-2.0 +# squabble.yml — hyperpolymath/cicd-squabbler in januskey's CI (squabble ≠ bypass: +# every green it reports is a required check that actually ran and passed). +# +# chains On every push, PR and weekly: map januskey's cross-repo CI +# dependencies (reusable workflows, actions) and probe each upstream +# once. Fails only on a blocking finding (exit 4): a dependency cycle, +# or a dead upstream, which produces no check run and so reads green. +# Its "pins N upstream refs by tag/branch" warning is expected here: +# januskey is an actions.lock repository, where the lock is the +# immutable resolution and direct refs stay version tags (standards +# governance-reusable.yml: "Do not also rewrite direct refs to raw SHAs"). +# fight On demand (Run workflow, with a PR number): classify each red check +# on that PR as self-win | escalate | assign-owner. Propose-only: no +# --apply, so it never writes, pushes or re-runs anything. Exit 3 (the +# base branch has no required_status_checks rule) is a finding about the +# rulesets, reported as a notice, not a failure. +# +# Not a required check. The action is pinned by commit, as its README requires; +# it installs squabble v0.1.0 only after its sha256 and its build-provenance +# attestation both check out (hence attestations: read). +name: Squabble + +on: + pull_request: + push: + branches: [main] + schedule: + - cron: '17 5 * * 1' # weekly, Monday 05:17 UTC + workflow_dispatch: + inputs: + pr: + description: 'Pull request number to fight (propose-only); leave empty to run chains' + required: false + default: '' + +permissions: + contents: read + +concurrency: + group: squabble-${{ github.ref }}-${{ inputs.pr }} + cancel-in-progress: true + +jobs: + chains: + name: CI dependency chains + if: github.event_name != 'workflow_dispatch' || inputs.pr == '' + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + attestations: read + steps: + - name: Checkout + uses: actions/checkout@v7.0.1 + with: + persist-credentials: false + + - name: squabble chains (fails only on a blocking finding) + uses: hyperpolymath/cicd-squabbler/.github/actions/squabble@b34cd53f9157d1b8a4f9c5c8d843846557f8622c # main 2026-10-12, squabble v0.1.0 + with: + args: | + chains + . + --follow + 1 + + fight: + name: squabble fight (propose-only) + if: github.event_name == 'workflow_dispatch' && inputs.pr != '' + runs-on: ubuntu-latest + timeout-minutes: 15 + permissions: + contents: read + attestations: read + actions: read + checks: read + pull-requests: read + statuses: read + steps: + - name: Checkout + uses: actions/checkout@v7.0.1 + with: + persist-credentials: false + + - name: squabble fight + id: fight + continue-on-error: true + uses: hyperpolymath/cicd-squabbler/.github/actions/squabble@b34cd53f9157d1b8a4f9c5c8d843846557f8622c # main 2026-10-12, squabble v0.1.0 + with: + args: | + fight + ${{ github.repository }} + ${{ inputs.pr }} + --repo-root + . + + - name: Read the verdict + env: + RC: ${{ steps.fight.outputs.exit-code }} + run: | + case "$RC" in + 0) echo "squabble fight: every required check on the PR is legitimately green or has a proposal above." ;; + 3) echo "::notice::squabble fight exit 3: the base branch has no required_status_checks rule. That is a ruleset finding, not a CI failure." ;; + *) echo "::error::squabble fight exited ${RC:-without running (install failed)}; see the step above."; exit 1 ;; + esac diff --git a/.github/workflows/workflow-linter.yml b/.github/workflows/workflow-linter.yml deleted file mode 100644 index 57208d9..0000000 --- a/.github/workflows/workflow-linter.yml +++ /dev/null @@ -1,57 +0,0 @@ -# This workflow is managed by gh actions-lock. -# SPDX-License-Identifier: MPL-2.0 -# This workflow is managed by gh actions-lock. -# Prevention workflow - validates all workflows have proper security config -name: Workflow Security Linter - -on: - pull_request: - paths: - - '.github/workflows/**' - push: - paths: - - '.github/workflows/**' - -permissions: read-all - -jobs: - lint-workflows: - runs-on: ubuntu-latest - timeout-minutes: 15 - steps: - - uses: actions/checkout@v7.0.1 - - - name: Check SPDX headers - run: | - errors=0 - for f in .github/workflows/*.yml .github/workflows/*.yaml; do - [ -f "$f" ] || continue - if ! head -1 "$f" | grep -q "SPDX-License-Identifier"; then - echo "ERROR: $f missing SPDX header" - errors=$((errors + 1)) - fi - done - exit $errors - - - name: Check permissions declaration - run: | - errors=0 - for f in .github/workflows/*.yml .github/workflows/*.yaml; do - [ -f "$f" ] || continue - if ! grep -q "^permissions:" "$f"; then - echo "ERROR: $f missing permissions declaration" - errors=$((errors + 1)) - fi - done - exit $errors - - - name: Check pinned actions - run: | - errors=0 - for f in .github/workflows/*.yml .github/workflows/*.yaml; do - [ -f "$f" ] || continue - # Look for uses: without SHA - if grep -E "uses:.*@v[0-9]" "$f" | grep -v "#"; then - echo "WARNING: $f has unpinned actions (missing SHA comment)" - fi - done diff --git a/benches/januskey_benchmarks.rs b/benches/januskey_benchmarks.rs index 21ec34c..461a225 100644 --- a/benches/januskey_benchmarks.rs +++ b/benches/januskey_benchmarks.rs @@ -173,7 +173,7 @@ fn bench_attestation(c: &mut Criterion) { group.bench_function("entry_generation", |b| { b.iter(|| { let entry = serde_json::json!({ - "key_id": "550e8400-e29b-41d4-a716-446655440000", + "key_id": "550e8400-e29b-81d4-a716-446655440000", "op": "key_gen", "algo": "aes256gcm", "timestamp": chrono::Utc::now().timestamp(), diff --git a/crates/januskey-cli/tests/p2p_test.rs b/crates/januskey-cli/tests/p2p_test.rs index e2a252d..d97b6bc 100644 --- a/crates/januskey-cli/tests/p2p_test.rs +++ b/crates/januskey-cli/tests/p2p_test.rs @@ -92,7 +92,7 @@ fn key_operation_creates_attestation_entry() { std::fs::create_dir_all(&attest_path).unwrap(); // Simulate key generation - let key_id = "550e8400-e29b-41d4-a716-446655440000"; + let key_id = "550e8400-e29b-81d4-a716-446655440000"; let entry = format!( r#"{{"op":"key_gen","key_id":"{}","algo":"aes256gcm","timestamp":{}}}"#, key_id, diff --git a/docs/security/KEY_LIFECYCLE.adoc b/docs/security/KEY_LIFECYCLE.adoc index 27cbe29..f3558c2 100644 --- a/docs/security/KEY_LIFECYCLE.adoc +++ b/docs/security/KEY_LIFECYCLE.adoc @@ -481,11 +481,11 @@ All key lifecycle events are logged: [source,json] ---- { - "event_id": "550e8400-e29b-41d4-a716-446655440000", + "event_id": "550e8400-e29b-81d4-a716-446655440000", "timestamp": "2025-01-15T14:30:00Z", "event_type": "KEY_ROTATED", - "key_id": "00000000-0000-4000-8000-000000000001", - "new_key_id": "00000000-0000-4000-8000-000000000002", + "key_id": "00000000-0000-8000-8000-000000000001", + "new_key_id": "00000000-0000-8000-8000-000000000002", "actor": "user@example.com", "reason": "scheduled_rotation", "metadata": { diff --git a/tests/p2p/component_p2p_test.rs b/tests/p2p/component_p2p_test.rs index fd520f7..12c500d 100644 --- a/tests/p2p/component_p2p_test.rs +++ b/tests/p2p/component_p2p_test.rs @@ -73,7 +73,7 @@ mod p2p_tests { std::fs::create_dir_all(&attest_path).unwrap(); // Simulate key generation - let key_id = "550e8400-e29b-41d4-a716-446655440000"; + let key_id = "550e8400-e29b-81d4-a716-446655440000"; let entry = format!( r#"{{"op":"key_gen","key_id":"{}","algo":"aes256gcm","timestamp":{}}}"#, key_id, From 2d053c67684e8332b177071826c21fcf170b123c Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 14 Oct 2026 00:44:33 +0000 Subject: [PATCH 2/4] chore(ffi): link the Zig FFI stub markers to #155 The eleven TODO(product) markers in ffi/zig/src/main.zig become TODO(#155), the issue that now tracks the stubbed crypto/CNO internals (#61 was closed by #63 with the lifecycle scaffold in place). The estate code-hygiene gate (cicd-suite code-hygiene-check, run by main-estate-audit.yml) accepts a debt marker only in MARKER(#issue) form; it failed on main on exactly these lines. Comments only. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Kjdf4Mf4Z41bNQh2TrsEp9 --- ffi/zig/src/main.zig | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ffi/zig/src/main.zig b/ffi/zig/src/main.zig index 2e0a96f..7bbb12e 100644 --- a/ffi/zig/src/main.zig +++ b/ffi/zig/src/main.zig @@ -15,7 +15,7 @@ // null-guard semantics that the conformance suite // (`ffi/zig/test/integration_test.zig`) exercises are implemented for real, // while the cryptographic / CNO product internals are stubbed and marked -// `TODO(product):`. Every public function returns a documented `JK_OK` / +// `TODO(#155):`. Every public function returns a documented `JK_OK` / // `JK_ERR_*` code from `Error` below. const std = @import("std"); @@ -183,7 +183,7 @@ pub export fn jk_init(path: ?[*:0]const u8, out_handle: ?*?*anyopaque) c_int { return code(.invalid_path); } - // TODO(product): create the on-disk `.januskey/` layout, content store, + // TODO(#155): create the on-disk `.januskey/` layout, content store, // metadata log and transaction directory for `path` (mirrors // crates/reversible-core JanusKey::init). The scaffold only allocates an // in-memory handle so the FFI lifecycle is exercisable end-to-end. @@ -203,7 +203,7 @@ pub export fn jk_init(path: ?[*:0]const u8, out_handle: ?*?*anyopaque) c_int { /// Open an existing repository. Scaffold shares `jk_init`'s behaviour. pub export fn jk_open(path: ?[*:0]const u8, out_handle: ?*?*anyopaque) c_int { - // TODO(product): require the repository to already exist and load its + // TODO(#155): require the repository to already exist and load its // state, rather than creating it. return jk_init(path, out_handle); } @@ -243,7 +243,7 @@ pub export fn jk_execute( _ = kind; _ = src; _ = dst; - // TODO(product): dispatch on `op` and execute the reversible operation + // TODO(#155): dispatch on `op` and execute the reversible operation // (copy/move/delete/modify/obliterate/key-*) recording inverse metadata, // delegating to the reversible-core executor. return code(.ok); @@ -253,7 +253,7 @@ pub export fn jk_execute( pub export fn jk_undo(handle: ?*anyopaque) c_int { const h = asHandle(handle) orelse return code(.not_initialized); if (!h.initialized) return code(.not_initialized); - // TODO(product): pop the last operation from the log and apply its + // TODO(#155): pop the last operation from the log and apply its // stored inverse (Theorem 3.4: Sequential Reversibility). return code(.ok); } @@ -269,7 +269,7 @@ pub export fn jk_obliterate( const h = asHandle(handle) orelse return code(.not_initialized); if (!h.initialized) return code(.not_initialized); if (path == null) return code(.invalid_path); - // TODO(product): perform the DoD 5220.22-M secure-overwrite passes and + // TODO(#155): perform the DoD 5220.22-M secure-overwrite passes and // compute the real commitment H(content_hash || nonce || timestamp) // (mirrors crates/januskey-cli/src/obliteration.rs). The scaffold returns // a structurally-valid proof so consumers can exercise the ABI. @@ -296,7 +296,7 @@ pub export fn jk_generate_key( if (!h.initialized) return code(.not_initialized); _ = algo; _ = passphrase; - // TODO(product): derive key material (Argon2id KDF + AEAD), persist the + // TODO(#155): derive key material (Argon2id KDF + AEAD), persist the // wrapped key, and return its UUID. if (out_id) |id| id.* = .{}; return code(.ok); @@ -313,7 +313,7 @@ pub export fn jk_rotate_key( if (!h.initialized) return code(.not_initialized); if (old_id == null) return code(.key_not_found); _ = new_passphrase; - // TODO(product): re-wrap content under the new key and retire the old one. + // TODO(#155): re-wrap content under the new key and retire the old one. if (out_new_id) |id| id.* = .{}; return code(.ok); } @@ -323,7 +323,7 @@ pub export fn jk_revoke_key(handle: ?*anyopaque, key_id: ?*const KeyId) c_int { const h = asHandle(handle) orelse return code(.not_initialized); if (!h.initialized) return code(.not_initialized); if (key_id == null) return code(.key_not_found); - // TODO(product): mark the key revoked in the key store / audit log. + // TODO(#155): mark the key revoked in the key store / audit log. return code(.ok); } @@ -359,7 +359,7 @@ pub export fn jk_tx_commit(handle: ?*anyopaque, tx: ?*anyopaque) c_int { const active = h.active_tx orelse return code(.tx_not_active); const given = asTx(tx) orelse return code(.tx_not_active); if (given != active) return code(.tx_not_active); - // TODO(product): durably commit the staged operations. + // TODO(#155): durably commit the staged operations. h.allocator.destroy(active); h.active_tx = null; return code(.ok); @@ -371,7 +371,7 @@ pub export fn jk_tx_rollback(handle: ?*anyopaque, tx: ?*anyopaque) c_int { const active = h.active_tx orelse return code(.tx_not_active); const given = asTx(tx) orelse return code(.tx_not_active); if (given != active) return code(.tx_not_active); - // TODO(product): undo the staged operations in reverse order. + // TODO(#155): undo the staged operations in reverse order. h.allocator.destroy(active); h.active_tx = null; return code(.ok); From e417e40c53f5e073acbe6eca517cd7a18f2bfcc2 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 14 Oct 2026 00:46:00 +0000 Subject: [PATCH 3/4] docs(wiki): convert docs/wiki from AsciiDoc to Markdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The estate formatting gate (cicd-suite formatting-check, run by main-estate-audit.yml) keeps .adoc for documentation but requires wiki content to be .md ("wikis are the one .md home"). It failed on main on these 14 pages. Mechanical conversion with asciidoctor (DocBook 5) then pandoc (GFM). The text is unchanged; only the markup is converted. - Headings: the document title becomes `#`, the rest shift down one level. - Tables (38) and code blocks (140) are carried across: one language-less listing became an indented block, the rest are fenced with their language. - stem:[…] latexmath becomes $…$ / $$…$$, which GitHub renders. - Wiki cross-links point at the .md files. - The SPDX and copyright lines become HTML comments. - One "GOVERNANCE +" that AsciiDoc read as a forced line break reads as a literal "+" again. Links that were already dead before this change (reversibility-theory, metadata-store, content-store, formal-proofs/reversibility) stay dead, now with .md extensions; no target page exists for them in either format. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Kjdf4Mf4Z41bNQh2TrsEp9 --- docs/wiki/Home.adoc | 129 ---- docs/wiki/Home.md | 93 +++ docs/wiki/api/{index.adoc => index.md} | 180 +++--- docs/wiki/architecture/index.adoc | 400 ------------ docs/wiki/architecture/index.md | 368 +++++++++++ docs/wiki/examples/{index.adoc => index.md} | 202 +++--- docs/wiki/formal-proofs/index.adoc | 359 ----------- docs/wiki/formal-proofs/index.md | 255 ++++++++ docs/wiki/guides/cli.adoc | 548 ---------------- docs/wiki/guides/cli.md | 447 +++++++++++++ .../{transactions.adoc => transactions.md} | 278 ++++---- docs/wiki/operations/index.adoc | 598 ------------------ docs/wiki/operations/index.md | 538 ++++++++++++++++ docs/wiki/theory/formal-model.adoc | 351 ---------- docs/wiki/theory/formal-model.md | 270 ++++++++ docs/wiki/theory/index.adoc | 133 ---- docs/wiki/theory/index.md | 113 ++++ docs/wiki/theory/maa-framework.adoc | 286 --------- docs/wiki/theory/maa-framework.md | 253 ++++++++ docs/wiki/theory/mpr.adoc | 275 -------- docs/wiki/theory/mpr.md | 211 ++++++ docs/wiki/theory/rmo-primitive.adoc | 335 ---------- docs/wiki/theory/rmo-primitive.md | 298 +++++++++ docs/wiki/theory/rmr-primitive.adoc | 387 ------------ docs/wiki/theory/rmr-primitive.md | 312 +++++++++ 25 files changed, 3456 insertions(+), 4163 deletions(-) delete mode 100644 docs/wiki/Home.adoc create mode 100644 docs/wiki/Home.md rename docs/wiki/api/{index.adoc => index.md} (89%) delete mode 100644 docs/wiki/architecture/index.adoc create mode 100644 docs/wiki/architecture/index.md rename docs/wiki/examples/{index.adoc => index.md} (83%) delete mode 100644 docs/wiki/formal-proofs/index.adoc create mode 100644 docs/wiki/formal-proofs/index.md delete mode 100644 docs/wiki/guides/cli.adoc create mode 100644 docs/wiki/guides/cli.md rename docs/wiki/guides/{transactions.adoc => transactions.md} (54%) delete mode 100644 docs/wiki/operations/index.adoc create mode 100644 docs/wiki/operations/index.md delete mode 100644 docs/wiki/theory/formal-model.adoc create mode 100644 docs/wiki/theory/formal-model.md delete mode 100644 docs/wiki/theory/index.adoc create mode 100644 docs/wiki/theory/index.md delete mode 100644 docs/wiki/theory/maa-framework.adoc create mode 100644 docs/wiki/theory/maa-framework.md delete mode 100644 docs/wiki/theory/mpr.adoc create mode 100644 docs/wiki/theory/mpr.md delete mode 100644 docs/wiki/theory/rmo-primitive.adoc create mode 100644 docs/wiki/theory/rmo-primitive.md delete mode 100644 docs/wiki/theory/rmr-primitive.adoc create mode 100644 docs/wiki/theory/rmr-primitive.md diff --git a/docs/wiki/Home.adoc b/docs/wiki/Home.adoc deleted file mode 100644 index 8a4b385..0000000 --- a/docs/wiki/Home.adoc +++ /dev/null @@ -1,129 +0,0 @@ -// SPDX-License-Identifier: CC-BY-SA-4.0 -// Copyright (c) Jonathan D.A. Jewell -= JanusKey Wiki -Jonathan D.A. Jewell -:toc: left -:toclevels: 3 -:icons: font -:source-highlighter: rouge -:experimental: - -== Welcome to JanusKey - -JanusKey is a reversible file operations utility (formal proofs pending) that pursues complete reversibility through architectural design rather than logging or backups. Unlike traditional backup systems that restore from external state, JanusKey operations carry sufficient information for inversion — the goal is to make data loss architecturally impossible. 30 Idris2 ABI proofs are landed; the reversibility theorems themselves are a target, not yet complete. - -=== Project Status (2026-06-12) - -Landed since 2026-04: estate standardization merged (flat contractiles, -`bot_directives/`, `6a2/anchor/`, `self-validating/`, GOVERNANCE + -MAINTAINERS + CODEOWNERS); license migrated PMPL-1.0-or-later → MPL-2.0 -(Cargo.toml aligned in #52); CI converted to SHA-pinned reusable-workflow -wrappers (rust-ci #39, hypatia-scan #40/#46, mirror #42, secret-scanner #43, -scorecard #44/#48) with cargo-audit + workflow-linter prevention workflows; -CodeQL cron now monthly; CHANGELOG.md seeded (#45); MCP-exposure threat -model drafted (status: draft-pending-human-review); Trustfile hardened to -the crypto-minimal baseline; 166 `.expect("TODO: handle error")` sites -cleared. CRG grade remains D (promotion path in READINESS.md). - -=== Core Philosophy - -JanusKey implements *Maximal Principle Reduction (MPR)*, a security methodology where vulnerability elimination occurs by construction rather than verification. This represents a fundamental shift from: - -[cols="1,1"] -|=== -| Traditional Approach | JanusKey Approach - -| Data protection through backups -| Data loss impossible by construction - -| Logging for accountability -| Structural reversibility guarantee - -| Trust in operational processes -| Formal mathematical proofs (in progress — Idris2 ABI proofs landed, reversibility theorems pending) - -| Recovery from external state -| Recovery from operation metadata -|=== - -=== Quick Navigation - -[cols="1,3"] -|=== -| Section | Description - -| link:theory/index.adoc[Theory] -| Formal foundations: MPR, MAA Framework, RMR/RMO primitives - -| link:architecture/index.adoc[Architecture] -| System design, component overview, data flow - -| link:operations/index.adoc[Operations Reference] -| Complete guide to all reversible operations - -| link:guides/cli.adoc[CLI Guide] -| Command-line interface documentation - -| link:guides/transactions.adoc[Transactions] -| Grouping operations with commit/rollback - -| link:api/index.adoc[API Reference] -| Rust library API documentation - -| link:formal-proofs/index.adoc[Formal Proofs] -| Mathematical proofs of reversibility - -| link:examples/index.adoc[Examples] -| Practical usage examples and patterns -|=== - -=== Key Guarantees (design targets — formal proofs pending) - -1. **Reversibility by design**: Every operation carries inversion metadata (formal proof of "always" is pending) -2. **Instant Rollback**: Undo changes in milliseconds, not hours -3. **Data loss impossible by construction**: the architectural goal the pending proofs are to establish -4. **Complete Audit Trail**: Every change tracked automatically -5. **Transaction Support**: Group operations, commit/rollback together -6. **Content-Addressed Storage**: SHA256 hashing with deduplication - -=== The Janus Symbol - -The name "JanusKey" derives from Janus, the Roman god of beginnings, endings, doorways, and transitions. Janus is typically depicted with two faces—one looking to the past, one to the future. This perfectly captures the essence of JanusKey: - -* **Forward Operations**: Execute changes moving from present state to future state -* **Reverse Operations**: Perfect restoration from any point back to the past - -The "Key" represents the cryptographic foundation (SHA256 content addressing) that ensures integrity and enables the formal guarantees of the system. - -=== Quick Start - -[source,bash] ----- -# Build from source -cd crates/januskey-cli -cargo build --release - -# Initialize in a directory -jk init - -# Delete files (reversible) -jk delete *.log - -# Instantly undo -jk undo - -# Group operations -jk begin "cleanup" -jk delete temp*.txt -jk move old/ archive/ -jk preview # Review changes -jk commit # Or: jk rollback ----- - -=== License - -JanusKey is dual-licensed under MIT OR MPL-2.0. - -=== Contributing - -See link:../../.github/CONTRIBUTING.md[../../.github/CONTRIBUTING.md] for guidelines. diff --git a/docs/wiki/Home.md b/docs/wiki/Home.md new file mode 100644 index 0000000..88dadde --- /dev/null +++ b/docs/wiki/Home.md @@ -0,0 +1,93 @@ + + + +# JanusKey Wiki + +## Welcome to JanusKey + +JanusKey is a reversible file operations utility (formal proofs pending) that pursues complete reversibility through architectural design rather than logging or backups. Unlike traditional backup systems that restore from external state, JanusKey operations carry sufficient information for inversion — the goal is to make data loss architecturally impossible. 30 Idris2 ABI proofs are landed; the reversibility theorems themselves are a target, not yet complete. + +### Project Status (2026-06-12) + +Landed since 2026-04: estate standardization merged (flat contractiles, `bot_directives/`, `6a2/anchor/`, `self-validating/`, GOVERNANCE + +MAINTAINERS + CODEOWNERS); license migrated PMPL-1.0-or-later → MPL-2.0 (Cargo.toml aligned in \#52); CI converted to SHA-pinned reusable-workflow wrappers (rust-ci \#39, hypatia-scan \#40/#46, mirror \#42, secret-scanner \#43, scorecard \#44/#48) with cargo-audit + workflow-linter prevention workflows; CodeQL cron now monthly; CHANGELOG.md seeded (#45); MCP-exposure threat model drafted (status: draft-pending-human-review); Trustfile hardened to the crypto-minimal baseline; 166 `.expect("TODO: handle error")` sites cleared. CRG grade remains D (promotion path in READINESS.md). + +### Core Philosophy + +JanusKey implements **Maximal Principle Reduction (MPR)**, a security methodology where vulnerability elimination occurs by construction rather than verification. This represents a fundamental shift from: + +| Traditional Approach | JanusKey Approach | +|---------------------------------|-----------------------------------------------------------------------------------------------------| +| Data protection through backups | Data loss impossible by construction | +| Logging for accountability | Structural reversibility guarantee | +| Trust in operational processes | Formal mathematical proofs (in progress — Idris2 ABI proofs landed, reversibility theorems pending) | +| Recovery from external state | Recovery from operation metadata | + +### Quick Navigation + +| Section | Description | +|-----------------------------------------------|------------------------------------------------------------| +| [Theory](theory/index.md) | Formal foundations: MPR, MAA Framework, RMR/RMO primitives | +| [Architecture](architecture/index.md) | System design, component overview, data flow | +| [Operations Reference](operations/index.md) | Complete guide to all reversible operations | +| [CLI Guide](guides/cli.md) | Command-line interface documentation | +| [Transactions](guides/transactions.md) | Grouping operations with commit/rollback | +| [API Reference](api/index.md) | Rust library API documentation | +| [Formal Proofs](formal-proofs/index.md) | Mathematical proofs of reversibility | +| [Examples](examples/index.md) | Practical usage examples and patterns | + +### Key Guarantees (design targets — formal proofs pending) + +1. **Reversibility by design**: Every operation carries inversion metadata (formal proof of "always" is pending) + +2. **Instant Rollback**: Undo changes in milliseconds, not hours + +3. **Data loss impossible by construction**: the architectural goal the pending proofs are to establish + +4. **Complete Audit Trail**: Every change tracked automatically + +5. **Transaction Support**: Group operations, commit/rollback together + +6. **Content-Addressed Storage**: SHA256 hashing with deduplication + +### The Janus Symbol + +The name "JanusKey" derives from Janus, the Roman god of beginnings, endings, doorways, and transitions. Janus is typically depicted with two faces—one looking to the past, one to the future. This perfectly captures the essence of JanusKey: + +- **Forward Operations**: Execute changes moving from present state to future state + +- **Reverse Operations**: Perfect restoration from any point back to the past + +The "Key" represents the cryptographic foundation (SHA256 content addressing) that ensures integrity and enables the formal guarantees of the system. + +### Quick Start + +``` bash +# Build from source +cd crates/januskey-cli +cargo build --release + +# Initialize in a directory +jk init + +# Delete files (reversible) +jk delete *.log + +# Instantly undo +jk undo + +# Group operations +jk begin "cleanup" +jk delete temp*.txt +jk move old/ archive/ +jk preview # Review changes +jk commit # Or: jk rollback +``` + +### License + +JanusKey is dual-licensed under MIT OR MPL-2.0. + +### Contributing + +See [../../.github/CONTRIBUTING.md](../../.github/CONTRIBUTING.md) for guidelines. diff --git a/docs/wiki/api/index.adoc b/docs/wiki/api/index.md similarity index 89% rename from docs/wiki/api/index.adoc rename to docs/wiki/api/index.md index 6a100dd..7c10cd2 100644 --- a/docs/wiki/api/index.adoc +++ b/docs/wiki/api/index.md @@ -1,36 +1,29 @@ -// SPDX-License-Identifier: CC-BY-SA-4.0 -// Copyright (c) Jonathan D.A. Jewell -= API Reference -:toc: left -:toclevels: 3 -:icons: font -:source-highlighter: rouge + + -== Overview +# API Reference + +## Overview JanusKey is implemented as a Rust library with a CLI frontend. This reference documents the public API for programmatic use. -== Crate Structure +## Crate Structure -[source] ----- -januskey/ -├── lib.rs # Main entry point -├── content_store.rs # Content-addressed storage -├── metadata.rs # Operation metadata and logging -├── operations.rs # File operations -├── transaction.rs # Transaction management -└── error.rs # Error types ----- + januskey/ + ├── lib.rs # Main entry point + ├── content_store.rs # Content-addressed storage + ├── metadata.rs # Operation metadata and logging + ├── operations.rs # File operations + ├── transaction.rs # Transaction management + └── error.rs # Error types -== Core Types +## Core Types -=== JanusKey +### JanusKey The main entry point for JanusKey functionality. -[source,rust] ----- +``` rust pub struct JanusKey { pub root: PathBuf, pub config: Config, @@ -49,12 +42,11 @@ impl JanusKey { /// Check if directory is initialized pub fn is_initialized(root: &Path) -> bool; } ----- +``` -==== Example +#### Example -[source,rust] ----- +``` rust use januskey::JanusKey; use std::path::Path; @@ -67,14 +59,13 @@ fn main() -> januskey::Result<()> { Ok(()) } ----- +``` -=== Config +### Config Configuration for JanusKey behavior. -[source,rust] ----- +``` rust #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Config { pub storage_path: PathBuf, @@ -93,16 +84,15 @@ impl Config { impl Default for Config { fn default() -> Self; } ----- +``` -== Content Store +## Content Store -=== ContentHash +### ContentHash SHA256 content hash with verification. -[source,rust] ----- +``` rust #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ContentHash(pub String); @@ -119,14 +109,13 @@ impl ContentHash { /// Verify content matches this hash pub fn verify(&self, content: &[u8]) -> bool; } ----- +``` -=== ContentStore +### ContentStore Content-addressed storage with optional compression. -[source,rust] ----- +``` rust pub struct ContentStore { root: PathBuf, compression: bool, @@ -157,12 +146,11 @@ impl ContentStore { /// Count stored content blobs pub fn count(&self) -> Result; } ----- +``` -==== Example +#### Example -[source,rust] ----- +``` rust use januskey::ContentStore; fn example() -> januskey::Result<()> { @@ -179,16 +167,15 @@ fn example() -> januskey::Result<()> { Ok(()) } ----- +``` -== Metadata +## Metadata -=== OperationType +### OperationType Enumeration of all operation types. -[source,rust] ----- +``` rust #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum OperationType { Delete, @@ -205,14 +192,13 @@ pub enum OperationType { Truncate, Touch, } ----- +``` -=== FileMetadata +### FileMetadata File metadata for preservation and restoration. -[source,rust] ----- +``` rust #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FileMetadata { pub permissions: u32, @@ -231,14 +217,13 @@ impl FileMetadata { /// Apply metadata to a file pub fn apply(&self, path: &Path) -> Result<()>; } ----- +``` -=== OperationMetadata +### OperationMetadata Complete metadata for an operation (sufficient for reversal). -[source,rust] ----- +``` rust #[derive(Debug, Clone, Serialize, Deserialize)] pub struct OperationMetadata { pub id: String, @@ -266,14 +251,13 @@ impl OperationMetadata { pub fn with_original_metadata(self, metadata: FileMetadata) -> Self; pub fn with_transaction_id(self, id: String) -> Self; } ----- +``` -=== MetadataStore +### MetadataStore Append-only operation log. -[source,rust] ----- +``` rust pub struct MetadataStore { path: PathBuf, log: OperationLog, @@ -315,16 +299,15 @@ impl MetadataStore { /// Prune old operations pub fn prune(&mut self, keep: usize) -> Result; } ----- +``` -== Operations +## Operations -=== FileOperation +### FileOperation Enumeration of all file operations. -[source,rust] ----- +``` rust #[derive(Debug, Clone)] pub enum FileOperation { Delete { path: PathBuf }, @@ -351,14 +334,13 @@ impl FileOperation { /// Get primary path pub fn path(&self) -> &Path; } ----- +``` -=== OperationExecutor +### OperationExecutor Executes operations with reversibility support. -[source,rust] ----- +``` rust pub struct OperationExecutor<'a> { content_store: &'a ContentStore, metadata_store: &'a mut MetadataStore, @@ -379,12 +361,11 @@ impl<'a> OperationExecutor<'a> { /// Undo an operation using its metadata pub fn undo(&mut self, operation_id: &str) -> Result; } ----- +``` -==== Example +#### Example -[source,rust] ----- +``` rust use januskey::{JanusKey, FileOperation, OperationExecutor}; use std::path::PathBuf; @@ -413,16 +394,15 @@ fn example() -> januskey::Result<()> { println!("File restored"); Ok(()) } ----- +``` -== Transactions +## Transactions -=== Transaction +### Transaction A transaction grouping multiple operations. -[source,rust] ----- +``` rust #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Transaction { pub id: String, @@ -448,14 +428,13 @@ impl Transaction { pub fn commit(&mut self); pub fn rollback(&mut self); } ----- +``` -=== TransactionManager +### TransactionManager Manages transaction lifecycle. -[source,rust] ----- +``` rust pub struct TransactionManager { path: PathBuf, log: TransactionLog, @@ -495,14 +474,13 @@ impl TransactionManager { /// Get active transaction ID pub fn active_id(&self) -> Option<&str>; } ----- +``` -=== TransactionExecutor +### TransactionExecutor Helper for executing operations within transactions. -[source,rust] ----- +``` rust pub struct TransactionExecutor<'a> { content_store: &'a ContentStore, metadata_store: &'a mut MetadataStore, @@ -519,12 +497,11 @@ impl<'a> TransactionExecutor<'a> { /// Execute an operation within the current transaction pub fn execute(&mut self, operation: FileOperation) -> Result; } ----- +``` -==== Example +#### Example -[source,rust] ----- +``` rust use januskey::{JanusKey, FileOperation, TransactionExecutor}; fn example() -> januskey::Result<()> { @@ -562,14 +539,13 @@ fn example() -> januskey::Result<()> { Ok(()) } ----- +``` -== Error Handling +## Error Handling -=== JanusError +### JanusError -[source,rust] ----- +``` rust #[derive(Debug, thiserror::Error)] pub enum JanusError { #[error("File not found: {0}")] @@ -613,10 +589,12 @@ pub enum JanusError { } pub type Result = std::result::Result; ----- +``` + +## Further Reading + +- [CLI Guide](../guides/cli.md) -== Further Reading +- [Operations Reference](../operations/index.md) -* link:../guides/cli.adoc[CLI Guide] -* link:../operations/index.adoc[Operations Reference] -* link:../theory/formal-model.adoc[Formal Model] +- [Formal Model](../theory/formal-model.md) diff --git a/docs/wiki/architecture/index.adoc b/docs/wiki/architecture/index.adoc deleted file mode 100644 index 7981476..0000000 --- a/docs/wiki/architecture/index.adoc +++ /dev/null @@ -1,400 +0,0 @@ -// SPDX-License-Identifier: CC-BY-SA-4.0 -// Copyright (c) Jonathan D.A. Jewell -= Architecture Overview -:toc: left -:toclevels: 3 -:icons: font -:source-highlighter: rouge - -== System Design - -JanusKey follows a layered architecture that separates concerns and enables the formal guarantees described in the theoretical foundations. - -[ditaa] -.... - +--------------------------------------------------+ - | CLI Layer | - | (jk command interface) | - +--------------------------------------------------+ - | - +--------------------------------------------------+ - | Operation Layer | - | (FileOperation enum, OperationExecutor) | - +--------------------------------------------------+ - | - +--------------------------------------------------+ - | Transaction Manager | - | (begin, commit, rollback) | - +--------------------------------------------------+ - | - +---------------+---------------+ - | | - +------v------+ +------v------+ - | Content | | Metadata | - | Store | | Store | - +------+------+ +------+------+ - | | - +------v------+ +------v------+ - | SHA256 Hash | | JSON Log | - | File System | | Append-Only| - +-------------+ +-------------+ -.... - -== Components - -=== CLI Layer - -Location: `crates/januskey-cli/src/main.rs` - -The CLI provides the user interface for JanusKey operations: - -[source,rust] ----- -#[derive(Parser)] -#[command(name = "jk")] -#[command(about = "JanusKey: Provably Reversible File Operations")] -struct Cli { - #[command(subcommand)] - command: Commands, -} - -enum Commands { - Init, // Initialize .januskey directory - Delete { files: Vec }, - Modify { path: PathBuf, content: String }, - Move { source: PathBuf, destination: PathBuf }, - Copy { source: PathBuf, destination: PathBuf }, - Mkdir { path: PathBuf, parents: bool }, - Rmdir { path: PathBuf, recursive: bool }, - // ... more commands - Undo { count: usize }, - Begin { name: Option }, - Commit, - Rollback, - History, - Status, -} ----- - -See: link:../guides/cli.adoc[CLI Guide] - -=== Operation Layer - -Location: `crates/januskey-cli/src/operations.rs` - -Defines all reversible file operations and their execution logic. - -==== FileOperation Enum - -[source,rust] ----- -pub enum FileOperation { - Delete { path: PathBuf }, - Modify { path: PathBuf, new_content: Vec }, - Move { source: PathBuf, destination: PathBuf }, - Copy { source: PathBuf, destination: PathBuf }, - Create { path: PathBuf, content: Vec }, - Chmod { path: PathBuf, new_mode: u32 }, - Mkdir { path: PathBuf, parents: bool }, - Rmdir { path: PathBuf }, - RmdirRecursive { path: PathBuf }, - Symlink { target: PathBuf, link_path: PathBuf }, - Append { path: PathBuf, content: Vec }, - Truncate { path: PathBuf, new_size: u64 }, - Touch { path: PathBuf, create: bool }, -} ----- - -==== OperationExecutor - -Handles the execution of operations with pre-capture and metadata recording: - -[source,rust] ----- -pub struct OperationExecutor<'a> { - content_store: &'a ContentStore, - metadata_store: &'a mut MetadataStore, - transaction_id: Option, -} - -impl OperationExecutor { - pub fn execute(&mut self, operation: FileOperation) -> Result; - pub fn undo(&mut self, operation_id: &str) -> Result; -} ----- - -See: link:../operations/index.adoc[Operations Reference] - -=== Transaction Manager - -Location: `crates/januskey-cli/src/transaction.rs` - -Provides transaction support for grouping operations. - -[source,rust] ----- -pub struct TransactionManager { - path: PathBuf, - log: TransactionLog, -} - -impl TransactionManager { - pub fn begin(&mut self, name: Option) -> Result<&Transaction>; - pub fn commit(&mut self) -> Result; - pub fn rollback( - &mut self, - content_store: &ContentStore, - metadata_store: &mut MetadataStore, - ) -> Result; - pub fn active(&self) -> Option<&Transaction>; -} ----- - -See: link:../guides/transactions.adoc[Transaction Guide] - -=== Content Store - -Location: `crates/januskey-cli/src/content_store.rs` - -Content-addressed storage with SHA256 hashing and optional compression. - -[source,rust] ----- -pub struct ContentStore { - root: PathBuf, - compression: bool, -} - -impl ContentStore { - pub fn store(&self, content: &[u8]) -> Result; - pub fn retrieve(&self, hash: &ContentHash) -> Result>; - pub fn exists(&self, hash: &ContentHash) -> bool; -} ----- - -==== Storage Layout - ----- -.januskey/content/ -├── ab/ -│ ├── cdef1234... # First 2 chars of hash as directory -│ └── cdef5678.gz # Compressed if enabled -├── cd/ -│ └── ef123456... -└── ... ----- - -See: link:content-store.adoc[Content Store Details] - -=== Metadata Store - -Location: `crates/januskey-cli/src/metadata.rs` - -Append-only operation log in JSON format. - -[source,rust] ----- -pub struct MetadataStore { - path: PathBuf, - log: OperationLog, -} - -pub struct OperationMetadata { - pub id: String, - pub op_type: OperationType, - pub timestamp: DateTime, - pub user: String, - pub path: PathBuf, - pub path_secondary: Option, - pub content_hash: Option, - pub original_metadata: Option, - pub undone: bool, - // ... -} ----- - -See: link:metadata-store.adoc[Metadata Store Details] - -== Data Flow - -=== Execute Operation Flow - -[ditaa] -.... - User Request - | - v - +----+----+ - | CLI | - +----+----+ - | - v - +----+--------+ - | Validate | - | Arguments | - +----+--------+ - | - v - +----+--------+ +-------------+ - | Pre-capture |---->| Content | - | Content | | Store | - +----+--------+ +-------------+ - | - v - +----+--------+ +-------------+ - | Record |---->| Metadata | - | Metadata | | Store | - +----+--------+ +-------------+ - | - v - +----+--------+ - | Execute | - | File Op | - +----+--------+ - | - v - Success Response -.... - -=== Undo Operation Flow - -[ditaa] -.... - Undo Request - | - v - +----+--------+ +-------------+ - | Load |<----| Metadata | - | Metadata | | Store | - +----+--------+ +-------------+ - | - v - +----+--------+ +-------------+ - | Retrieve |<----| Content | - | Content | | Store | - +----+--------+ +-------------+ - | - v - +----+--------+ - | Construct | - | Inverse Op | - +----+--------+ - | - v - +----+--------+ - | Execute | - | Inverse | - +----+--------+ - | - v - +----+--------+ +-------------+ - | Mark |---->| Metadata | - | Undone | | Store | - +----+--------+ +-------------+ - | - v - Success Response -.... - -== Directory Structure - ----- -project/ -├── .januskey/ -│ ├── config.json # Configuration -│ ├── content/ # Content-addressed storage -│ │ ├── ab/ -│ │ ├── cd/ -│ │ └── ... -│ ├── metadata.json # Operation log -│ └── transactions # Transaction log -└── ... (project files) ----- - -== Configuration - -Located at `.januskey/config.json`: - -[source,json] ----- -{ - "storage_path": "/home/user/.local/share/januskey", - "compression": true, - "max_history": 10000, - "auto_confirm": false, - "dry_run_default": false, - "audit_enabled": true -} ----- - -|=== -| Option | Default | Description - -| compression -| true -| Enable gzip compression for stored content - -| max_history -| 10000 -| Maximum operations to keep in history - -| auto_confirm -| false -| Skip confirmation for dangerous operations - -| dry_run_default -| false -| Default to dry-run mode - -| audit_enabled -| true -| Enable audit logging -|=== - -== Error Handling - -JanusKey uses a custom error type for comprehensive error handling: - -[source,rust] ----- -pub enum JanusError { - FileNotFound(String), - PathExists(String), - DirectoryNotFound(String), - NotInitialized(String), - ContentIntegrityError { expected: String, actual: String }, - MetadataCorrupted(String), - TransactionActive(String), - NoActiveTransaction, - InvalidOperationId(String), - OperationFailed(String), - Io(std::io::Error), - // ... -} ----- - -== Security Model - -=== Integrity - -* Content verified on retrieval via SHA256 -* Metadata stored atomically -* Operations fail-safe (fail before corrupting state) - -=== Confidentiality - -* Content stored locally (no network transmission) -* File permissions preserved -* No secrets in metadata - -=== Availability - -* Self-contained (no external dependencies) -* Deduplication prevents storage exhaustion -* Garbage collection for old content - -== Further Reading - -* link:content-store.adoc[Content Store Deep Dive] -* link:metadata-store.adoc[Metadata Store Deep Dive] -* link:../theory/index.adoc[Theoretical Foundations] diff --git a/docs/wiki/architecture/index.md b/docs/wiki/architecture/index.md new file mode 100644 index 0000000..733ed9f --- /dev/null +++ b/docs/wiki/architecture/index.md @@ -0,0 +1,368 @@ + + + +# Architecture Overview + +## System Design + +JanusKey follows a layered architecture that separates concerns and enables the formal guarantees described in the theoretical foundations. + + +--------------------------------------------------+ + | CLI Layer | + | (jk command interface) | + +--------------------------------------------------+ + | + +--------------------------------------------------+ + | Operation Layer | + | (FileOperation enum, OperationExecutor) | + +--------------------------------------------------+ + | + +--------------------------------------------------+ + | Transaction Manager | + | (begin, commit, rollback) | + +--------------------------------------------------+ + | + +---------------+---------------+ + | | + +------v------+ +------v------+ + | Content | | Metadata | + | Store | | Store | + +------+------+ +------+------+ + | | + +------v------+ +------v------+ + | SHA256 Hash | | JSON Log | + | File System | | Append-Only| + +-------------+ +-------------+ + +## Components + +### CLI Layer + +Location: `crates/januskey-cli/src/main.rs` + +The CLI provides the user interface for JanusKey operations: + +``` rust +#[derive(Parser)] +#[command(name = "jk")] +#[command(about = "JanusKey: Provably Reversible File Operations")] +struct Cli { + #[command(subcommand)] + command: Commands, +} + +enum Commands { + Init, // Initialize .januskey directory + Delete { files: Vec }, + Modify { path: PathBuf, content: String }, + Move { source: PathBuf, destination: PathBuf }, + Copy { source: PathBuf, destination: PathBuf }, + Mkdir { path: PathBuf, parents: bool }, + Rmdir { path: PathBuf, recursive: bool }, + // ... more commands + Undo { count: usize }, + Begin { name: Option }, + Commit, + Rollback, + History, + Status, +} +``` + +See: [CLI Guide](../guides/cli.md) + +### Operation Layer + +Location: `crates/januskey-cli/src/operations.rs` + +Defines all reversible file operations and their execution logic. + +#### FileOperation Enum + +``` rust +pub enum FileOperation { + Delete { path: PathBuf }, + Modify { path: PathBuf, new_content: Vec }, + Move { source: PathBuf, destination: PathBuf }, + Copy { source: PathBuf, destination: PathBuf }, + Create { path: PathBuf, content: Vec }, + Chmod { path: PathBuf, new_mode: u32 }, + Mkdir { path: PathBuf, parents: bool }, + Rmdir { path: PathBuf }, + RmdirRecursive { path: PathBuf }, + Symlink { target: PathBuf, link_path: PathBuf }, + Append { path: PathBuf, content: Vec }, + Truncate { path: PathBuf, new_size: u64 }, + Touch { path: PathBuf, create: bool }, +} +``` + +#### OperationExecutor + +Handles the execution of operations with pre-capture and metadata recording: + +``` rust +pub struct OperationExecutor<'a> { + content_store: &'a ContentStore, + metadata_store: &'a mut MetadataStore, + transaction_id: Option, +} + +impl OperationExecutor { + pub fn execute(&mut self, operation: FileOperation) -> Result; + pub fn undo(&mut self, operation_id: &str) -> Result; +} +``` + +See: [Operations Reference](../operations/index.md) + +### Transaction Manager + +Location: `crates/januskey-cli/src/transaction.rs` + +Provides transaction support for grouping operations. + +``` rust +pub struct TransactionManager { + path: PathBuf, + log: TransactionLog, +} + +impl TransactionManager { + pub fn begin(&mut self, name: Option) -> Result<&Transaction>; + pub fn commit(&mut self) -> Result; + pub fn rollback( + &mut self, + content_store: &ContentStore, + metadata_store: &mut MetadataStore, + ) -> Result; + pub fn active(&self) -> Option<&Transaction>; +} +``` + +See: [Transaction Guide](../guides/transactions.md) + +### Content Store + +Location: `crates/januskey-cli/src/content_store.rs` + +Content-addressed storage with SHA256 hashing and optional compression. + +``` rust +pub struct ContentStore { + root: PathBuf, + compression: bool, +} + +impl ContentStore { + pub fn store(&self, content: &[u8]) -> Result; + pub fn retrieve(&self, hash: &ContentHash) -> Result>; + pub fn exists(&self, hash: &ContentHash) -> bool; +} +``` + +#### Storage Layout + + .januskey/content/ + ├── ab/ + │ ├── cdef1234... # First 2 chars of hash as directory + │ └── cdef5678.gz # Compressed if enabled + ├── cd/ + │ └── ef123456... + └── ... + +See: [Content Store Details](content-store.md) + +### Metadata Store + +Location: `crates/januskey-cli/src/metadata.rs` + +Append-only operation log in JSON format. + +``` rust +pub struct MetadataStore { + path: PathBuf, + log: OperationLog, +} + +pub struct OperationMetadata { + pub id: String, + pub op_type: OperationType, + pub timestamp: DateTime, + pub user: String, + pub path: PathBuf, + pub path_secondary: Option, + pub content_hash: Option, + pub original_metadata: Option, + pub undone: bool, + // ... +} +``` + +See: [Metadata Store Details](metadata-store.md) + +## Data Flow + +### Execute Operation Flow + + User Request + | + v + +----+----+ + | CLI | + +----+----+ + | + v + +----+--------+ + | Validate | + | Arguments | + +----+--------+ + | + v + +----+--------+ +-------------+ + | Pre-capture |---->| Content | + | Content | | Store | + +----+--------+ +-------------+ + | + v + +----+--------+ +-------------+ + | Record |---->| Metadata | + | Metadata | | Store | + +----+--------+ +-------------+ + | + v + +----+--------+ + | Execute | + | File Op | + +----+--------+ + | + v + Success Response + +### Undo Operation Flow + + Undo Request + | + v + +----+--------+ +-------------+ + | Load |<----| Metadata | + | Metadata | | Store | + +----+--------+ +-------------+ + | + v + +----+--------+ +-------------+ + | Retrieve |<----| Content | + | Content | | Store | + +----+--------+ +-------------+ + | + v + +----+--------+ + | Construct | + | Inverse Op | + +----+--------+ + | + v + +----+--------+ + | Execute | + | Inverse | + +----+--------+ + | + v + +----+--------+ +-------------+ + | Mark |---->| Metadata | + | Undone | | Store | + +----+--------+ +-------------+ + | + v + Success Response + +## Directory Structure + + project/ + ├── .januskey/ + │ ├── config.json # Configuration + │ ├── content/ # Content-addressed storage + │ │ ├── ab/ + │ │ ├── cd/ + │ │ └── ... + │ ├── metadata.json # Operation log + │ └── transactions # Transaction log + └── ... (project files) + +## Configuration + +Located at `.januskey/config.json`: + +``` json +{ + "storage_path": "/home/user/.local/share/januskey", + "compression": true, + "max_history": 10000, + "auto_confirm": false, + "dry_run_default": false, + "audit_enabled": true +} +``` + +| Option | Default | Description | +|-----------------|---------|--------------------------------------------| +| compression | true | Enable gzip compression for stored content | +| max_history | 10000 | Maximum operations to keep in history | +| auto_confirm | false | Skip confirmation for dangerous operations | +| dry_run_default | false | Default to dry-run mode | +| audit_enabled | true | Enable audit logging | + +## Error Handling + +JanusKey uses a custom error type for comprehensive error handling: + +``` rust +pub enum JanusError { + FileNotFound(String), + PathExists(String), + DirectoryNotFound(String), + NotInitialized(String), + ContentIntegrityError { expected: String, actual: String }, + MetadataCorrupted(String), + TransactionActive(String), + NoActiveTransaction, + InvalidOperationId(String), + OperationFailed(String), + Io(std::io::Error), + // ... +} +``` + +## Security Model + +### Integrity + +- Content verified on retrieval via SHA256 + +- Metadata stored atomically + +- Operations fail-safe (fail before corrupting state) + +### Confidentiality + +- Content stored locally (no network transmission) + +- File permissions preserved + +- No secrets in metadata + +### Availability + +- Self-contained (no external dependencies) + +- Deduplication prevents storage exhaustion + +- Garbage collection for old content + +## Further Reading + +- [Content Store Deep Dive](content-store.md) + +- [Metadata Store Deep Dive](metadata-store.md) + +- [Theoretical Foundations](../theory/index.md) diff --git a/docs/wiki/examples/index.adoc b/docs/wiki/examples/index.md similarity index 83% rename from docs/wiki/examples/index.adoc rename to docs/wiki/examples/index.md index b7d3fe2..c09eea2 100644 --- a/docs/wiki/examples/index.adoc +++ b/docs/wiki/examples/index.md @@ -1,21 +1,17 @@ -// SPDX-License-Identifier: CC-BY-SA-4.0 -// Copyright (c) Jonathan D.A. Jewell -= Examples and Use Cases -:toc: left -:toclevels: 3 -:icons: font -:source-highlighter: rouge + + -== Overview +# Examples and Use Cases + +## Overview This section provides practical examples of JanusKey usage for common scenarios. -== Basic Operations +## Basic Operations -=== Safe File Deletion +### Safe File Deletion -[source,bash] ----- +``` bash # Initialize JanusKey jk init @@ -29,12 +25,11 @@ jk history --type DELETE --count 50 jk undo --id abc123 # File restored ----- +``` -=== Modifying Configuration Files +### Modifying Configuration Files -[source,bash] ----- +``` bash # Modify a config file jk modify config.yaml --sed 's/debug: true/debug: false/' @@ -45,12 +40,11 @@ jk undo jk modify config.yaml --sed 's/log_level: INFO/log_level: DEBUG/' # Works! Keep it ----- +``` -=== Reorganizing Directory Structure +### Reorganizing Directory Structure -[source,bash] ----- +``` bash jk begin "reorganize project" # Move files to new structure @@ -74,14 +68,13 @@ else jk rollback echo "Tests failed, reverted changes" fi ----- +``` -== Advanced Patterns +## Advanced Patterns -=== Batch Processing with Rollback +### Batch Processing with Rollback -[source,bash] ----- +``` bash #!/bin/bash # batch_process.sh - Process files with automatic rollback on error @@ -101,12 +94,11 @@ done jk commit echo "All files processed successfully" ----- +``` -=== Safe Deployment Preparation +### Safe Deployment Preparation -[source,bash] ----- +``` bash #!/bin/bash # prepare_deploy.sh @@ -135,12 +127,11 @@ else echo "Build/test failed, changes reverted" exit 1 fi ----- +``` -=== Configuration Migration +### Configuration Migration -[source,bash] ----- +``` bash #!/bin/bash # migrate_config.sh - Migrate config format with safety @@ -169,14 +160,13 @@ else echo "Validation failed, reverted" exit 1 fi ----- +``` -== Integration Examples +## Integration Examples -=== Git Pre-Commit Hook +### Git Pre-Commit Hook -[source,bash] ----- +``` bash #!/bin/bash # .git/hooks/pre-commit @@ -200,12 +190,11 @@ if ! git diff --quiet; then else jk rollback # No changes needed fi ----- +``` -=== CI/CD Pipeline Integration +### CI/CD Pipeline Integration -[source,yaml] ----- +``` yaml # .github/workflows/test.yml name: Test with JanusKey Protection @@ -237,57 +226,55 @@ jobs: echo "Tests modified files unexpectedly" fi exit $TEST_RESULT ----- +``` -=== Makefile Integration +### Makefile Integration -[source,makefile] ----- +``` makefile # Makefile with JanusKey support .PHONY: clean build test deploy init: - jk init + jk init clean: - jk begin "clean" - jk delete -r build/ - jk delete -r dist/ - jk delete "*.pyc" - jk commit + jk begin "clean" + jk delete -r build/ + jk delete -r dist/ + jk delete "*.pyc" + jk commit build: clean - jk begin "build" - python setup.py build - npm run build - jk commit + jk begin "build" + python setup.py build + npm run build + jk commit test: - jk begin "test" - pytest tests/ - npm test - @if [ $$? -eq 0 ]; then \ - jk commit; \ - else \ - jk rollback; \ - exit 1; \ - fi + jk begin "test" + pytest tests/ + npm test + @if [ $$? -eq 0 ]; then \ + jk commit; \ + else \ + jk rollback; \ + exit 1; \ + fi deploy: build test - jk begin "deploy prep" - jk delete .env.local - jk delete -r __pycache__/ - # ... deployment steps - jk commit ----- + jk begin "deploy prep" + jk delete .env.local + jk delete -r __pycache__/ + # ... deployment steps + jk commit +``` -== Rust Library Examples +## Rust Library Examples -=== Basic Library Usage +### Basic Library Usage -[source,rust] ----- +``` rust use januskey::{JanusKey, FileOperation, OperationExecutor}; use std::path::PathBuf; @@ -316,12 +303,11 @@ fn main() -> januskey::Result<()> { Ok(()) } ----- +``` -=== Transaction Example +### Transaction Example -[source,rust] ----- +``` rust use januskey::{JanusKey, FileOperation, TransactionExecutor}; fn batch_cleanup(jk: &mut JanusKey) -> januskey::Result<()> { @@ -359,12 +345,11 @@ fn batch_cleanup(jk: &mut JanusKey) -> januskey::Result<()> { Ok(()) } ----- +``` -=== Custom Operation Wrapper +### Custom Operation Wrapper -[source,rust] ----- +``` rust use januskey::{JanusKey, FileOperation, OperationExecutor, OperationMetadata}; use std::path::Path; @@ -427,14 +412,13 @@ impl SafeFileOps { executor.undo(&id) } } ----- +``` -== Real-World Scenarios +## Real-World Scenarios -=== Log Rotation with Recovery +### Log Rotation with Recovery -[source,bash] ----- +``` bash #!/bin/bash # rotate_logs.sh - Rotate logs with ability to recover @@ -464,12 +448,11 @@ echo "Log rotation complete" # If something went wrong with the app after rotation: # jk rollback # This restores all logs to their original locations ----- +``` -=== Database Migration Companion +### Database Migration Companion -[source,bash] ----- +``` bash #!/bin/bash # migrate_with_files.sh - Database migration with file changes @@ -491,14 +474,13 @@ else echo "Note: Database changes may need manual rollback" exit 1 fi ----- +``` -== Troubleshooting Examples +## Troubleshooting Examples -=== Recovering from Mistakes +### Recovering from Mistakes -[source,bash] ----- +``` bash # Accidentally deleted important files $ rm -rf src/ # Oops! Wrong directory @@ -508,12 +490,11 @@ $ jk history --path "src/*" --count 100 $ jk undo --count 47 # Undo all src/ deletions # Files restored! ----- +``` -=== Viewing What Changed +### Viewing What Changed -[source,bash] ----- +``` bash # What did I change in the last hour? $ jk history --since "1 hour ago" @@ -522,12 +503,11 @@ $ jk history --path "config/*" # Details on a specific operation $ jk info abc123 ----- +``` -=== Cleaning Up History +### Cleaning Up History -[source,bash] ----- +``` bash # Check current storage usage $ jk status Content store: 156 MB (2341 blobs) @@ -539,10 +519,12 @@ Would remove: 89 MB (1205 blobs) $ jk gc --older-than 30d Removed: 89 MB ----- +``` + +## Further Reading + +- [CLI Reference](../guides/cli.md) -== Further Reading +- [Transaction Guide](../guides/transactions.md) -* link:../guides/cli.adoc[CLI Reference] -* link:../guides/transactions.adoc[Transaction Guide] -* link:../api/index.adoc[API Reference] +- [API Reference](../api/index.md) diff --git a/docs/wiki/formal-proofs/index.adoc b/docs/wiki/formal-proofs/index.adoc deleted file mode 100644 index b92f6e1..0000000 --- a/docs/wiki/formal-proofs/index.adoc +++ /dev/null @@ -1,359 +0,0 @@ -// SPDX-License-Identifier: CC-BY-SA-4.0 -// Copyright (c) Jonathan D.A. Jewell -= Formal Proofs -:toc: left -:toclevels: 3 -:icons: font -:source-highlighter: rouge -:stem: latexmath - -== Overview - -This section provides rigorous mathematical proofs of JanusKey's reversibility guarantees. These proofs form the foundation of the system's correctness claims. - -== Notation - -|=== -| Symbol | Meaning - -| stem:[S] -| File system state - -| stem:[\mathcal{P}] -| Set of all valid paths - -| stem:[\mathcal{C}] -| Set of all content values - -| stem:[\mathcal{M}] -| Set of all metadata values - -| stem:[O] -| Operation - -| stem:[O^{-1}] -| Inverse operation - -| stem:[\bot] -| Non-existent / undefined - -| stem:[H] -| Hash function (SHA256) -|=== - -== Fundamental Theorems - -=== Theorem 1: Individual Operation Reversibility - -**Statement**: Every JanusKey operation stem:[O] has an inverse stem:[O^{-1}] such that: - -[stem] -++++ -\forall S \in \mathcal{S}: O^{-1}(O(S)) = S -++++ - -**Proof**: - -We prove by case analysis on operation type. - -==== Case: DELETE - -Let stem:[O = \text{DELETE}_p] where stem:[S(p) = (c, m)]. - -Before execution, JanusKey: -1. Stores stem:[c] in content store: stem:[\Sigma(H(c)) := c] -2. Records stem:[m] in metadata -3. Executes: stem:[S'(p) := \bot] - -The inverse stem:[O^{-1} = \text{CREATE}_{p,c,m}]: -1. Retrieves stem:[c = \Sigma(H(c))] -2. Retrieves stem:[m] from metadata -3. Executes: stem:[S''(p) := (c, m)] - -Since stem:[S''(p) = (c, m) = S(p)], and no other paths are modified, stem:[S'' = S]. ∎ - -==== Case: MODIFY - -Let stem:[O = \text{MODIFY}_{p,c'}] where stem:[S(p) = (c, m)]. - -Before execution, JanusKey: -1. Stores stem:[c] in content store -2. Records stem:[H(c)] in metadata -3. Executes: stem:[S'(p) := (c', m)] - -The inverse stem:[O^{-1} = \text{MODIFY}_{p,c}]: -1. Retrieves stem:[c = \Sigma(H(c))] -2. Executes: stem:[S''(p) := (c, m)] - -Since stem:[S''(p) = S(p)], we have stem:[S'' = S]. ∎ - -==== Case: MOVE - -Let stem:[O = \text{MOVE}_{p,q}] where stem:[S(p) = (c, m)] and stem:[S(q) = \bot]. - -Execution: -1. Records stem:[p] and stem:[q] in metadata -2. Executes: stem:[S'(p) := \bot], stem:[S'(q) := (c, m)] - -The inverse stem:[O^{-1} = \text{MOVE}_{q,p}]: -1. Executes: stem:[S''(q) := \bot], stem:[S''(p) := (c, m)] - -Since stem:[S''(p) = S(p)] and stem:[S''(q) = S(q) = \bot], we have stem:[S'' = S]. ∎ - -==== Case: COPY - -Let stem:[O = \text{COPY}_{p,q}] where stem:[S(q) = \bot]. - -Execution creates stem:[S'(q) := S(p)]. - -The inverse stem:[O^{-1} = \text{DELETE}_q]: -1. Executes: stem:[S''(q) := \bot] - -Since stem:[S''(q) = S(q)], we have stem:[S'' = S]. ∎ - -==== Case: MKDIR, RMDIR, APPEND, TRUNCATE, etc. - -Similar proofs follow the pattern: -1. Capture state before modification -2. Store captured state -3. Define inverse using stored state -4. Show inverse restores original state ∎ - -=== Theorem 2: Composition Reversibility - -**Statement**: If stem:[O_1, O_2] are reversible operations, then stem:[O_2 \circ O_1] is reversible with inverse stem:[O_1^{-1} \circ O_2^{-1}]. - -**Proof**: - -Let stem:[S] be the initial state. - -[stem] -++++ -\begin{align} -&(O_1^{-1} \circ O_2^{-1})(O_2 \circ O_1)(S) \\ -&= (O_1^{-1} \circ O_2^{-1})(O_2(O_1(S))) \\ -&= O_1^{-1}(O_2^{-1}(O_2(O_1(S)))) \\ -&= O_1^{-1}(O_1(S)) & \text{(by reversibility of } O_2\text{)} \\ -&= S & \text{(by reversibility of } O_1\text{)} -\end{align} -++++ - -∎ - -=== Theorem 3: Sequential Reversibility - -**Statement** (Theorem 3.4): For any sequence of reversible operations stem:[\langle O_1, O_2, \ldots, O_n \rangle]: - -[stem] -++++ -(O_n^{-1} \circ \cdots \circ O_1^{-1}) \circ (O_1 \circ \cdots \circ O_n) = \text{id} -++++ - -**Proof**: - -By induction on stem:[n]. - -**Base case** (stem:[n = 1]): - -stem:[O_1^{-1} \circ O_1 = \text{id}] by Theorem 1. ∎ - -**Inductive step**: - -Assume the theorem holds for stem:[n-1] operations. - -Let stem:[C_{n-1} = O_1 \circ \cdots \circ O_{n-1}] and stem:[C_{n-1}^{-1} = O_{n-1}^{-1} \circ \cdots \circ O_1^{-1}]. - -By the inductive hypothesis: stem:[C_{n-1}^{-1} \circ C_{n-1} = \text{id}]. - -Now consider stem:[n] operations: - -[stem] -++++ -\begin{align} -&(O_n^{-1} \circ C_{n-1}^{-1}) \circ (C_{n-1} \circ O_n) \\ -&= O_n^{-1} \circ (C_{n-1}^{-1} \circ C_{n-1}) \circ O_n & \text{(associativity)} \\ -&= O_n^{-1} \circ \text{id} \circ O_n & \text{(inductive hypothesis)} \\ -&= O_n^{-1} \circ O_n \\ -&= \text{id} & \text{(Theorem 1)} -\end{align} -++++ - -∎ - -=== Theorem 4: Content Integrity - -**Statement**: For any content stem:[c], if stored and later retrieved: - -[stem] -++++ -\text{retrieve}(\text{store}(c)) = c -++++ - -**Proof**: - -Let stem:[h = H(c)] where stem:[H] is SHA256. - -1. stem:[\text{store}(c)] writes stem:[c] to path derived from stem:[h] and returns stem:[h] -2. stem:[\text{retrieve}(h)] reads from path derived from stem:[h], yielding some stem:[c'] -3. stem:[\text{retrieve}] verifies stem:[H(c') = h] -4. By collision resistance of SHA256: stem:[H(c') = H(c) = h \Rightarrow c' = c] with overwhelming probability - -∎ - -=== Theorem 5: Transaction Atomicity - -**Statement**: A transaction stem:[T = \langle O_1, \ldots, O_n \rangle] satisfies: - -Either all operations are applied (commit) or the state equals the initial state (rollback). - -**Proof**: - -**Case: Commit** - -All operations stem:[O_1, \ldots, O_n] are executed. By definition, commit finalizes the transaction without state modification. - -**Case: Rollback** - -Rollback executes stem:[\langle O_n^{-1}, \ldots, O_1^{-1} \rangle]. - -By Theorem 3 (Sequential Reversibility): - -[stem] -++++ -(O_n^{-1} \circ \cdots \circ O_1^{-1}) \circ (O_1 \circ \cdots \circ O_n)(S) = S -++++ - -The final state equals the initial state. ∎ - -== Auxiliary Lemmas - -=== Lemma: Metadata Sufficiency - -**Statement**: For every operation stem:[O], the recorded metadata stem:[\mu] contains sufficient information to compute stem:[O^{-1}]. - -**Proof**: - -By construction of `OperationMetadata`: - -|=== -| Operation | Required for Inverse | Stored in Metadata - -| DELETE -| Content, metadata -| `content_hash`, `original_metadata` - -| MODIFY -| Original content -| `content_hash` - -| MOVE -| Original path -| `path` (source), `path_secondary` (dest) - -| COPY -| Destination path -| `path_secondary` - -| MKDIR -| Path -| `path` - -| RMDIR (recursive) -| All contents -| `content_hash` (manifest) - -| APPEND -| Original size -| `original_metadata.size` - -| TRUNCATE -| Original content -| `content_hash` -|=== - -All required information is stored. ∎ - -=== Lemma: Content Availability - -**Statement**: For every content hash stem:[h] in operation metadata, stem:[\Sigma(h) \neq \bot]. - -**Proof**: - -By the execution protocol: - -1. Content is stored in stem:[\Sigma] before being referenced in metadata -2. Storage returns hash only on success -3. Metadata is recorded only after successful storage - -Therefore, any hash in metadata references existing content. ∎ - -=== Lemma: Hash Uniqueness - -**Statement**: stem:[H] produces unique identifiers for distinct content with overwhelming probability. - -**Proof**: - -SHA256 has output space of stem:[2^{256}]. The birthday bound for collision is approximately stem:[2^{128}] operations, far exceeding practical usage. ∎ - -== Complexity Analysis - -=== Space Complexity - -For stem:[n] operations on files of average size stem:[s]: - -* Content store: stem:[O(n \cdot s)] (with deduplication, often less) -* Metadata store: stem:[O(n \cdot k)] where stem:[k] is constant metadata size - -=== Time Complexity - -|=== -| Operation | Execute | Undo - -| DELETE -| stem:[O(s)] (read + hash + store) -| stem:[O(s)] (retrieve + write) - -| MODIFY -| stem:[O(s)] (read + hash + store + write) -| stem:[O(s)] (retrieve + write) - -| MOVE -| stem:[O(1)] (rename) -| stem:[O(1)] (rename) - -| COPY -| stem:[O(s)] (copy) -| stem:[O(s)] (delete) -|=== - -== Verification Status - -|=== -| Theorem | Proof Status | Machine Verified - -| Individual Reversibility -| Complete -| Planned (Rocq) - -| Composition Reversibility -| Complete -| Planned (Rocq) - -| Sequential Reversibility -| Complete -| Planned (Rocq) - -| Content Integrity -| Complete -| Relies on SHA256 - -| Transaction Atomicity -| Complete -| Planned (Rocq) -|=== - -== References - -1. Bennett, C.H. (1973). "Logical Reversibility of Computation" -2. Landauer, R. (1961). "Irreversibility and Heat Generation in the Computing Process" -3. NIST FIPS 180-4: Secure Hash Standard (SHA-256) diff --git a/docs/wiki/formal-proofs/index.md b/docs/wiki/formal-proofs/index.md new file mode 100644 index 0000000..6aae5dc --- /dev/null +++ b/docs/wiki/formal-proofs/index.md @@ -0,0 +1,255 @@ + + + +# Formal Proofs + +## Overview + +This section provides rigorous mathematical proofs of JanusKey’s reversibility guarantees. These proofs form the foundation of the system’s correctness claims. + +## Notation + +| Symbol | Meaning | +|---------------|----------------------------| +| $S$ | File system state | +| $\mathcal{P}$ | Set of all valid paths | +| $\mathcal{C}$ | Set of all content values | +| $\mathcal{M}$ | Set of all metadata values | +| $O$ | Operation | +| $O^{-1}$ | Inverse operation | +| $\bot$ | Non-existent / undefined | +| $H$ | Hash function (SHA256) | + +## Fundamental Theorems + +### Theorem 1: Individual Operation Reversibility + +**Statement**: Every JanusKey operation $O$ has an inverse $O^{-1}$ such that: + +$$\forall S \in \mathcal{S}: O^{-1}(O(S)) = S$$ + +**Proof**: + +We prove by case analysis on operation type. + +#### Case: DELETE + +Let $O = \text{DELETE}_p$ where $S(p) = (c, m)$. + +Before execution, JanusKey: 1. Stores $c$ in content store: $\Sigma(H(c)) := c$ 2. Records $m$ in metadata 3. Executes: $S'(p) := \bot$ + +The inverse $O^{-1} = \text{CREATE}_{p,c,m}$: 1. Retrieves $c = \Sigma(H(c))$ 2. Retrieves $m$ from metadata 3. Executes: $S''(p) := (c, m)$ + +Since $S''(p) = (c, m) = S(p)$, and no other paths are modified, $S'' = S$. ∎ + +#### Case: MODIFY + +Let $O = \text{MODIFY}_{p,c'}$ where $S(p) = (c, m)$. + +Before execution, JanusKey: 1. Stores $c$ in content store 2. Records $H(c)$ in metadata 3. Executes: $S'(p) := (c', m)$ + +The inverse $O^{-1} = \text{MODIFY}_{p,c}$: 1. Retrieves $c = \Sigma(H(c))$ 2. Executes: $S''(p) := (c, m)$ + +Since $S''(p) = S(p)$, we have $S'' = S$. ∎ + +#### Case: MOVE + +Let $O = \text{MOVE}_{p,q}$ where $S(p) = (c, m)$ and $S(q) = \bot$. + +Execution: 1. Records $p$ and $q$ in metadata 2. Executes: $S'(p) := \bot$, $S'(q) := (c, m)$ + +The inverse $O^{-1} = \text{MOVE}_{q,p}$: 1. Executes: $S''(q) := \bot$, $S''(p) := (c, m)$ + +Since $S''(p) = S(p)$ and $S''(q) = S(q) = \bot$, we have $S'' = S$. ∎ + +#### Case: COPY + +Let $O = \text{COPY}_{p,q}$ where $S(q) = \bot$. + +Execution creates $S'(q) := S(p)$. + +The inverse $O^{-1} = \text{DELETE}_q$: 1. Executes: $S''(q) := \bot$ + +Since $S''(q) = S(q)$, we have $S'' = S$. ∎ + +#### Case: MKDIR, RMDIR, APPEND, TRUNCATE, etc. + +Similar proofs follow the pattern: 1. Capture state before modification 2. Store captured state 3. Define inverse using stored state 4. Show inverse restores original state ∎ + +### Theorem 2: Composition Reversibility + +**Statement**: If $O_1, O_2$ are reversible operations, then $O_2 \circ O_1$ is reversible with inverse $O_1^{-1} \circ O_2^{-1}$. + +**Proof**: + +Let $S$ be the initial state. + +$$\begin{align} +&(O_1^{-1} \circ O_2^{-1})(O_2 \circ O_1)(S) \\ +&= (O_1^{-1} \circ O_2^{-1})(O_2(O_1(S))) \\ +&= O_1^{-1}(O_2^{-1}(O_2(O_1(S)))) \\ +&= O_1^{-1}(O_1(S)) & \text{(by reversibility of } O_2\text{)} \\ +&= S & \text{(by reversibility of } O_1\text{)} +\end{align}$$ + +∎ + +### Theorem 3: Sequential Reversibility + +**Statement** (Theorem 3.4): For any sequence of reversible operations $\langle O_1, O_2, \ldots, O_n \rangle$: + +$$(O_n^{-1} \circ \cdots \circ O_1^{-1}) \circ (O_1 \circ \cdots \circ O_n) = \text{id}$$ + +**Proof**: + +By induction on $n$. + +**Base case** ($n = 1$): + +$O_1^{-1} \circ O_1 = \text{id}$ by Theorem 1. ∎ + +**Inductive step**: + +Assume the theorem holds for $n-1$ operations. + +Let $C_{n-1} = O_1 \circ \cdots \circ O_{n-1}$ and $C_{n-1}^{-1} = O_{n-1}^{-1} \circ \cdots \circ O_1^{-1}$. + +By the inductive hypothesis: $C_{n-1}^{-1} \circ C_{n-1} = \text{id}$. + +Now consider $n$ operations: + +$$\begin{align} +&(O_n^{-1} \circ C_{n-1}^{-1}) \circ (C_{n-1} \circ O_n) \\ +&= O_n^{-1} \circ (C_{n-1}^{-1} \circ C_{n-1}) \circ O_n & \text{(associativity)} \\ +&= O_n^{-1} \circ \text{id} \circ O_n & \text{(inductive hypothesis)} \\ +&= O_n^{-1} \circ O_n \\ +&= \text{id} & \text{(Theorem 1)} +\end{align}$$ + +∎ + +### Theorem 4: Content Integrity + +**Statement**: For any content $c$, if stored and later retrieved: + +$$\text{retrieve}(\text{store}(c)) = c$$ + +**Proof**: + +Let $h = H(c)$ where $H$ is SHA256. + +1. $\text{store}(c)$ writes $c$ to path derived from $h$ and returns $h$ + +2. $\text{retrieve}(h)$ reads from path derived from $h$, yielding some $c'$ + +3. $\text{retrieve}$ verifies $H(c') = h$ + +4. By collision resistance of SHA256: $H(c') = H(c) = h \Rightarrow c' = c$ with overwhelming probability + +∎ + +### Theorem 5: Transaction Atomicity + +**Statement**: A transaction $T = \langle O_1, \ldots, O_n \rangle$ satisfies: + +Either all operations are applied (commit) or the state equals the initial state (rollback). + +**Proof**: + +**Case: Commit** + +All operations $O_1, \ldots, O_n$ are executed. By definition, commit finalizes the transaction without state modification. + +**Case: Rollback** + +Rollback executes $\langle O_n^{-1}, \ldots, O_1^{-1} \rangle$. + +By Theorem 3 (Sequential Reversibility): + +$$(O_n^{-1} \circ \cdots \circ O_1^{-1}) \circ (O_1 \circ \cdots \circ O_n)(S) = S$$ + +The final state equals the initial state. ∎ + +## Auxiliary Lemmas + +### Lemma: Metadata Sufficiency + +**Statement**: For every operation $O$, the recorded metadata $\mu$ contains sufficient information to compute $O^{-1}$. + +**Proof**: + +By construction of `OperationMetadata`: + +| Operation | Required for Inverse | Stored in Metadata | +|-------------------|----------------------|------------------------------------------| +| DELETE | Content, metadata | `content_hash`, `original_metadata` | +| MODIFY | Original content | `content_hash` | +| MOVE | Original path | `path` (source), `path_secondary` (dest) | +| COPY | Destination path | `path_secondary` | +| MKDIR | Path | `path` | +| RMDIR (recursive) | All contents | `content_hash` (manifest) | +| APPEND | Original size | `original_metadata.size` | +| TRUNCATE | Original content | `content_hash` | + +All required information is stored. ∎ + +### Lemma: Content Availability + +**Statement**: For every content hash $h$ in operation metadata, $\Sigma(h) \neq \bot$. + +**Proof**: + +By the execution protocol: + +1. Content is stored in $\Sigma$ before being referenced in metadata + +2. Storage returns hash only on success + +3. Metadata is recorded only after successful storage + +Therefore, any hash in metadata references existing content. ∎ + +### Lemma: Hash Uniqueness + +**Statement**: $H$ produces unique identifiers for distinct content with overwhelming probability. + +**Proof**: + +SHA256 has output space of $2^{256}$. The birthday bound for collision is approximately $2^{128}$ operations, far exceeding practical usage. ∎ + +## Complexity Analysis + +### Space Complexity + +For $n$ operations on files of average size $s$: + +- Content store: $O(n \cdot s)$ (with deduplication, often less) + +- Metadata store: $O(n \cdot k)$ where $k$ is constant metadata size + +### Time Complexity + +| Operation | Execute | Undo | +|-----------|--------------------------------------|---------------------------| +| DELETE | $O(s)$ (read + hash + store) | $O(s)$ (retrieve + write) | +| MODIFY | $O(s)$ (read + hash + store + write) | $O(s)$ (retrieve + write) | +| MOVE | $O(1)$ (rename) | $O(1)$ (rename) | +| COPY | $O(s)$ (copy) | $O(s)$ (delete) | + +## Verification Status + +| Theorem | Proof Status | Machine Verified | +|---------------------------|--------------|------------------| +| Individual Reversibility | Complete | Planned (Rocq) | +| Composition Reversibility | Complete | Planned (Rocq) | +| Sequential Reversibility | Complete | Planned (Rocq) | +| Content Integrity | Complete | Relies on SHA256 | +| Transaction Atomicity | Complete | Planned (Rocq) | + +## References + +1. Bennett, C.H. (1973). "Logical Reversibility of Computation" + +2. Landauer, R. (1961). "Irreversibility and Heat Generation in the Computing Process" + +3. NIST FIPS 180-4: Secure Hash Standard (SHA-256) diff --git a/docs/wiki/guides/cli.adoc b/docs/wiki/guides/cli.adoc deleted file mode 100644 index 9a92e21..0000000 --- a/docs/wiki/guides/cli.adoc +++ /dev/null @@ -1,548 +0,0 @@ -// SPDX-License-Identifier: CC-BY-SA-4.0 -// Copyright (c) Jonathan D.A. Jewell -= CLI Commands Guide -:toc: left -:toclevels: 3 -:icons: font -:source-highlighter: rouge - -== Overview - -The `jk` command-line interface provides access to all JanusKey operations. This guide covers every command with examples. - -== Getting Started - -=== Installation - -[source,bash] ----- -# Build from source -cd crates/januskey-cli -cargo build --release - -# Install to PATH -cargo install --path . - -# Verify installation -jk --version ----- - -=== Initialize a Directory - -Before using JanusKey in a directory, initialize it: - -[source,bash] ----- -cd /your/project -jk init ----- - -This creates `.januskey/` with: ----- -.januskey/ -├── config.json # Configuration -├── content/ # Content store -├── metadata.json # Operation log -└── transactions # Transaction log ----- - -== Core Commands - -=== init - -Initialize JanusKey in the current directory. - -[source,bash] ----- -jk init ----- - -Options: -|=== -| Flag | Description - -| `--force` -| Reinitialize existing .januskey directory -|=== - -=== delete - -Delete files (reversible). - -[source,bash] ----- -jk delete file.txt -jk delete *.log -jk delete -f important.txt # No confirmation -jk delete --dry-run test/* # Preview only ----- - -Options: -|=== -| Flag | Description - -| `-f, --force` -| Skip confirmation - -| `--dry-run` -| Preview without executing - -| `-r, --recursive` -| Delete directories recursively -|=== - -=== modify - -Modify file content. - -[source,bash] ----- -jk modify file.txt --content "new content" -jk modify file.txt --file replacement.txt -jk modify config.yaml --sed "s/DEBUG/INFO/g" ----- - -Options: -|=== -| Flag | Description - -| `--content ` -| New content as string - -| `--file ` -| Read new content from file - -| `--sed ` -| Apply sed-like substitution -|=== - -=== move - -Move or rename files. - -[source,bash] ----- -jk move old.txt new.txt -jk mv src/file.txt dst/file.txt ----- - -=== copy - -Copy files. - -[source,bash] ----- -jk copy source.txt destination.txt -jk cp original.txt backup.txt ----- - -=== mkdir - -Create directories. - -[source,bash] ----- -jk mkdir new_directory -jk mkdir -p path/to/nested/directory ----- - -Options: -|=== -| Flag | Description - -| `-p, --parents` -| Create parent directories as needed -|=== - -=== rmdir - -Remove directories. - -[source,bash] ----- -jk rmdir empty_dir -jk rmdir -r directory_with_contents ----- - -Options: -|=== -| Flag | Description - -| `-r, --recursive` -| Remove directory and all contents -|=== - -=== symlink - -Create symbolic links (Unix only). - -[source,bash] ----- -jk symlink /path/to/target link_name -jk ln -s target link ----- - -=== append - -Append content to files. - -[source,bash] ----- -jk append log.txt --content "New log entry" -jk append data.csv --file new_rows.csv ----- - -=== truncate - -Truncate files to specified size. - -[source,bash] ----- -jk truncate large_file.txt --size 1000 -jk truncate file.txt -s 0 # Empty the file ----- - -=== touch - -Update timestamps or create empty files. - -[source,bash] ----- -jk touch file.txt # Update mtime or create -jk touch --no-create file # Only update, don't create ----- - -=== chmod - -Change file permissions (Unix only). - -[source,bash] ----- -jk chmod 644 file.txt -jk chmod +x script.sh -jk chmod 755 directory/ ----- - -== Undo Commands - -=== undo - -Undo the last operation(s). - -[source,bash] ----- -jk undo # Undo last operation -jk undo --count 3 # Undo last 3 operations -jk undo -n 5 # Undo last 5 operations -jk undo --id abc123 # Undo specific operation by ID ----- - -Options: -|=== -| Flag | Description - -| `-n, --count ` -| Number of operations to undo - -| `--id ` -| Undo specific operation by ID - -| `--dry-run` -| Preview undo without executing -|=== - -Example session: -[source,bash] ----- -$ jk delete important.txt -Deleted: important.txt (sha256:abc123...) - -$ ls important.txt -ls: cannot access 'important.txt': No such file or directory - -$ jk undo -Restored: important.txt - -$ ls important.txt -important.txt ----- - -== Transaction Commands - -=== begin - -Start a new transaction. - -[source,bash] ----- -jk begin -jk begin "cleanup operation" -jk begin --name "refactor" ----- - -=== commit - -Commit the current transaction. - -[source,bash] ----- -jk commit ----- - -=== rollback - -Rollback all operations in the current transaction. - -[source,bash] ----- -jk rollback ----- - -=== preview - -Preview operations in the current transaction. - -[source,bash] ----- -jk preview ----- - -Example output: ----- -Transaction: cleanup operation -Started: 2025-12-17T10:30:00Z -Operations pending: - 1. DELETE /tmp/old_logs/app.log - 2. DELETE /tmp/old_logs/error.log - 3. MOVE /config/old.yaml → /archive/old.yaml -Total files affected: 4 ----- - -== Information Commands - -=== status - -Show current JanusKey status. - -[source,bash] ----- -jk status ----- - -Example output: ----- -JanusKey Status -=============== -Directory: /home/user/project -Initialized: Yes -Operations: 156 -Content store: 12 MB (89 blobs) -Active transaction: cleanup (3 operations) ----- - -=== history - -Show operation history. - -[source,bash] ----- -jk history -jk history --count 20 -jk history --type DELETE -jk history --path "*.log" -jk history --since "2025-12-01" ----- - -Options: -|=== -| Flag | Description - -| `-n, --count ` -| Show last N operations - -| `--type ` -| Filter by operation type - -| `--path ` -| Filter by path pattern - -| `--since ` -| Show operations since date - -| `--json` -| Output as JSON -|=== - -Example output: ----- -Operation History (last 10) -=========================== -ID Type Path Time --------- ------- ---------------------- ------------------- -abc123 DELETE /tmp/test.txt 2025-12-17 10:30 -def456 MODIFY /config/app.yaml 2025-12-17 10:25 -ghi789 MOVE /old/path → /new/path 2025-12-17 10:20 -... ----- - -=== info - -Show details about a specific operation. - -[source,bash] ----- -jk info abc123 -jk info --id abc123 ----- - -Example output: ----- -Operation: abc123 -Type: DELETE -Time: 2025-12-17T10:30:00Z -User: jonathan -Path: /tmp/test.txt -Content Hash: sha256:abc123... -Original Size: 1234 bytes -Original Permissions: 644 -Undone: No ----- - -== Maintenance Commands - -=== gc - -Run garbage collection to reclaim space. - -[source,bash] ----- -jk gc -jk gc --dry-run -jk gc --older-than 30d ----- - -Options: -|=== -| Flag | Description - -| `--dry-run` -| Show what would be deleted - -| `--older-than ` -| Only GC content older than duration -|=== - -=== verify - -Verify content store integrity. - -[source,bash] ----- -jk verify -jk verify --fix ----- - -=== config - -View or modify configuration. - -[source,bash] ----- -jk config # Show all config -jk config compression # Get specific value -jk config compression false # Set value -jk config --reset # Reset to defaults ----- - -== Global Options - -These options work with all commands: - -|=== -| Flag | Description - -| `-v, --verbose` -| Verbose output - -| `-q, --quiet` -| Suppress non-error output - -| `--dry-run` -| Preview without executing - -| `--json` -| Output as JSON - -| `-C, --directory ` -| Run in specified directory - -| `--help` -| Show help for command - -| `--version` -| Show version -|=== - -== Exit Codes - -|=== -| Code | Meaning - -| 0 -| Success - -| 1 -| General error - -| 2 -| Invalid arguments - -| 3 -| File not found - -| 4 -| Permission denied - -| 5 -| Not initialized - -| 6 -| Transaction error -|=== - -== Shell Completion - -Generate shell completions: - -[source,bash] ----- -# Bash -jk completions bash > /etc/bash_completion.d/jk - -# Zsh -jk completions zsh > ~/.zsh/completions/_jk - -# Fish -jk completions fish > ~/.config/fish/completions/jk.fish ----- - -== Aliases - -You can create shell aliases for common operations: - -[source,bash] ----- -# ~/.bashrc or ~/.zshrc -alias jkd='jk delete' -alias jkm='jk move' -alias jkc='jk copy' -alias jku='jk undo' -alias jks='jk status' -alias jkh='jk history' ----- - -== Further Reading - -* link:transactions.adoc[Transaction Guide] -* link:../operations/index.adoc[Operations Reference] -* link:../examples/index.adoc[Examples] diff --git a/docs/wiki/guides/cli.md b/docs/wiki/guides/cli.md new file mode 100644 index 0000000..8788757 --- /dev/null +++ b/docs/wiki/guides/cli.md @@ -0,0 +1,447 @@ + + + +# CLI Commands Guide + +## Overview + +The `jk` command-line interface provides access to all JanusKey operations. This guide covers every command with examples. + +## Getting Started + +### Installation + +``` bash +# Build from source +cd crates/januskey-cli +cargo build --release + +# Install to PATH +cargo install --path . + +# Verify installation +jk --version +``` + +### Initialize a Directory + +Before using JanusKey in a directory, initialize it: + +``` bash +cd /your/project +jk init +``` + +This creates `.januskey/` with: + + .januskey/ + ├── config.json # Configuration + ├── content/ # Content store + ├── metadata.json # Operation log + └── transactions # Transaction log + +## Core Commands + +### init + +Initialize JanusKey in the current directory. + +``` bash +jk init +``` + +Options: + +| Flag | Description | +|-----------|-------------------------------------------| +| `--force` | Reinitialize existing .januskey directory | + +### delete + +Delete files (reversible). + +``` bash +jk delete file.txt +jk delete *.log +jk delete -f important.txt # No confirmation +jk delete --dry-run test/* # Preview only +``` + +Options: + +| Flag | Description | +|-------------------|--------------------------------| +| `-f, --force` | Skip confirmation | +| `--dry-run` | Preview without executing | +| `-r, --recursive` | Delete directories recursively | + +### modify + +Modify file content. + +``` bash +jk modify file.txt --content "new content" +jk modify file.txt --file replacement.txt +jk modify config.yaml --sed "s/DEBUG/INFO/g" +``` + +Options: + +| Flag | Description | +|--------------------|-----------------------------| +| `--content ` | New content as string | +| `--file ` | Read new content from file | +| `--sed ` | Apply sed-like substitution | + +### move + +Move or rename files. + +``` bash +jk move old.txt new.txt +jk mv src/file.txt dst/file.txt +``` + +### copy + +Copy files. + +``` bash +jk copy source.txt destination.txt +jk cp original.txt backup.txt +``` + +### mkdir + +Create directories. + +``` bash +jk mkdir new_directory +jk mkdir -p path/to/nested/directory +``` + +Options: + +| Flag | Description | +|-----------------|-------------------------------------| +| `-p, --parents` | Create parent directories as needed | + +### rmdir + +Remove directories. + +``` bash +jk rmdir empty_dir +jk rmdir -r directory_with_contents +``` + +Options: + +| Flag | Description | +|-------------------|-----------------------------------| +| `-r, --recursive` | Remove directory and all contents | + +### symlink + +Create symbolic links (Unix only). + +``` bash +jk symlink /path/to/target link_name +jk ln -s target link +``` + +### append + +Append content to files. + +``` bash +jk append log.txt --content "New log entry" +jk append data.csv --file new_rows.csv +``` + +### truncate + +Truncate files to specified size. + +``` bash +jk truncate large_file.txt --size 1000 +jk truncate file.txt -s 0 # Empty the file +``` + +### touch + +Update timestamps or create empty files. + +``` bash +jk touch file.txt # Update mtime or create +jk touch --no-create file # Only update, don't create +``` + +### chmod + +Change file permissions (Unix only). + +``` bash +jk chmod 644 file.txt +jk chmod +x script.sh +jk chmod 755 directory/ +``` + +## Undo Commands + +### undo + +Undo the last operation(s). + +``` bash +jk undo # Undo last operation +jk undo --count 3 # Undo last 3 operations +jk undo -n 5 # Undo last 5 operations +jk undo --id abc123 # Undo specific operation by ID +``` + +Options: + +| Flag | Description | +|-------------------|--------------------------------| +| `-n, --count ` | Number of operations to undo | +| `--id ` | Undo specific operation by ID | +| `--dry-run` | Preview undo without executing | + +Example session: + +``` bash +$ jk delete important.txt +Deleted: important.txt (sha256:abc123...) + +$ ls important.txt +ls: cannot access 'important.txt': No such file or directory + +$ jk undo +Restored: important.txt + +$ ls important.txt +important.txt +``` + +## Transaction Commands + +### begin + +Start a new transaction. + +``` bash +jk begin +jk begin "cleanup operation" +jk begin --name "refactor" +``` + +### commit + +Commit the current transaction. + +``` bash +jk commit +``` + +### rollback + +Rollback all operations in the current transaction. + +``` bash +jk rollback +``` + +### preview + +Preview operations in the current transaction. + +``` bash +jk preview +``` + +Example output: + + Transaction: cleanup operation + Started: 2025-12-17T10:30:00Z + Operations pending: + 1. DELETE /tmp/old_logs/app.log + 2. DELETE /tmp/old_logs/error.log + 3. MOVE /config/old.yaml → /archive/old.yaml + Total files affected: 4 + +## Information Commands + +### status + +Show current JanusKey status. + +``` bash +jk status +``` + +Example output: + + JanusKey Status + =============== + Directory: /home/user/project + Initialized: Yes + Operations: 156 + Content store: 12 MB (89 blobs) + Active transaction: cleanup (3 operations) + +### history + +Show operation history. + +``` bash +jk history +jk history --count 20 +jk history --type DELETE +jk history --path "*.log" +jk history --since "2025-12-01" +``` + +Options: + +| Flag | Description | +|--------------------|----------------------------| +| `-n, --count ` | Show last N operations | +| `--type ` | Filter by operation type | +| `--path ` | Filter by path pattern | +| `--since ` | Show operations since date | +| `--json` | Output as JSON | + +Example output: + + Operation History (last 10) + =========================== + ID Type Path Time + -------- ------- ---------------------- ------------------- + abc123 DELETE /tmp/test.txt 2025-12-17 10:30 + def456 MODIFY /config/app.yaml 2025-12-17 10:25 + ghi789 MOVE /old/path → /new/path 2025-12-17 10:20 + ... + +### info + +Show details about a specific operation. + +``` bash +jk info abc123 +jk info --id abc123 +``` + +Example output: + + Operation: abc123 + Type: DELETE + Time: 2025-12-17T10:30:00Z + User: jonathan + Path: /tmp/test.txt + Content Hash: sha256:abc123... + Original Size: 1234 bytes + Original Permissions: 644 + Undone: No + +## Maintenance Commands + +### gc + +Run garbage collection to reclaim space. + +``` bash +jk gc +jk gc --dry-run +jk gc --older-than 30d +``` + +Options: + +| Flag | Description | +|---------------------------|-------------------------------------| +| `--dry-run` | Show what would be deleted | +| `--older-than ` | Only GC content older than duration | + +### verify + +Verify content store integrity. + +``` bash +jk verify +jk verify --fix +``` + +### config + +View or modify configuration. + +``` bash +jk config # Show all config +jk config compression # Get specific value +jk config compression false # Set value +jk config --reset # Reset to defaults +``` + +## Global Options + +These options work with all commands: + +| Flag | Description | +|-------------------------|----------------------------| +| `-v, --verbose` | Verbose output | +| `-q, --quiet` | Suppress non-error output | +| `--dry-run` | Preview without executing | +| `--json` | Output as JSON | +| `-C, --directory ` | Run in specified directory | +| `--help` | Show help for command | +| `--version` | Show version | + +## Exit Codes + +| Code | Meaning | +|------|-------------------| +| 0 | Success | +| 1 | General error | +| 2 | Invalid arguments | +| 3 | File not found | +| 4 | Permission denied | +| 5 | Not initialized | +| 6 | Transaction error | + +## Shell Completion + +Generate shell completions: + +``` bash +# Bash +jk completions bash > /etc/bash_completion.d/jk + +# Zsh +jk completions zsh > ~/.zsh/completions/_jk + +# Fish +jk completions fish > ~/.config/fish/completions/jk.fish +``` + +## Aliases + +You can create shell aliases for common operations: + +``` bash +# ~/.bashrc or ~/.zshrc +alias jkd='jk delete' +alias jkm='jk move' +alias jkc='jk copy' +alias jku='jk undo' +alias jks='jk status' +alias jkh='jk history' +``` + +## Further Reading + +- [Transaction Guide](transactions.md) + +- [Operations Reference](../operations/index.md) + +- [Examples](../examples/index.md) diff --git a/docs/wiki/guides/transactions.adoc b/docs/wiki/guides/transactions.md similarity index 54% rename from docs/wiki/guides/transactions.adoc rename to docs/wiki/guides/transactions.md index 07d0f4d..407c86d 100644 --- a/docs/wiki/guides/transactions.adoc +++ b/docs/wiki/guides/transactions.md @@ -1,180 +1,157 @@ -// SPDX-License-Identifier: CC-BY-SA-4.0 -// Copyright (c) Jonathan D.A. Jewell -= Transaction Guide -:toc: left -:toclevels: 3 -:icons: font -:source-highlighter: rouge + + -== Overview +# Transaction Guide + +## Overview Transactions allow you to group multiple operations into an atomic unit that can be committed or rolled back as a whole. This implements the Sequential Reversibility guarantee (Theorem 3.4). -== Basic Usage +## Basic Usage -=== Starting a Transaction +### Starting a Transaction -[source,bash] ----- +``` bash jk begin # or with a name jk begin "cleanup old files" ----- +``` -=== Performing Operations +### Performing Operations While a transaction is active, all operations are tracked: -[source,bash] ----- +``` bash jk begin "refactor" jk delete old_module/*.py jk move config/old.yaml config/legacy.yaml jk modify README.md --content "Updated documentation" ----- +``` -=== Previewing Changes +### Previewing Changes Before committing, review what will happen: -[source,bash] ----- +``` bash jk preview ----- +``` Output: ----- -Transaction: refactor -Started: 2025-12-17T10:30:00Z -User: jonathan -Operations: - 1. DELETE old_module/utils.py - 2. DELETE old_module/helpers.py - 3. DELETE old_module/__init__.py - 4. MOVE config/old.yaml → config/legacy.yaml - 5. MODIFY README.md + Transaction: refactor + Started: 2025-12-17T10:30:00Z + User: jonathan + + Operations: + 1. DELETE old_module/utils.py + 2. DELETE old_module/helpers.py + 3. DELETE old_module/__init__.py + 4. MOVE config/old.yaml → config/legacy.yaml + 5. MODIFY README.md -Files affected: 5 -Content stored: 12.3 KB ----- + Files affected: 5 + Content stored: 12.3 KB -=== Committing +### Committing Make the changes permanent: -[source,bash] ----- +``` bash jk commit ----- +``` After commit, individual operations can still be undone, but not as a group. -=== Rolling Back +### Rolling Back Undo all operations in the transaction: -[source,bash] ----- +``` bash jk rollback ----- +``` This reverses all operations in reverse order, restoring the original state completely. -== Transaction States - -[ditaa] -.... - +----------+ - | Begin | - +----+-----+ - | - v - +----+-----+ - | Active |<----+ - +----+-----+ | - | | - +----+----+------+ - | | - v v -+---+---+ +---+---+ -| Commit| |Rollback| -+---+---+ +---+---+ - | | - v v -+---+---+ +---+---+ -|Committed| |RolledBack| -+---------+ +----------+ -.... - -|=== -| State | Description - -| Active -| Transaction accepting operations - -| Committed -| Operations finalized - -| RolledBack -| Operations reversed -|=== - -== Transaction Rules - -=== Single Active Transaction +## Transaction States + + +----------+ + | Begin | + +----+-----+ + | + v + +----+-----+ + | Active |<----+ + +----+-----+ | + | | + +----+----+------+ + | | + v v + +---+---+ +---+---+ + | Commit| |Rollback| + +---+---+ +---+---+ + | | + v v + +---+---+ +---+---+ + |Committed| |RolledBack| + +---------+ +----------+ + +| State | Description | +|------------|----------------------------------| +| Active | Transaction accepting operations | +| Committed | Operations finalized | +| RolledBack | Operations reversed | + +## Transaction Rules + +### Single Active Transaction Only one transaction can be active at a time: -[source,bash] ----- +``` bash $ jk begin "first" Transaction started: first $ jk begin "second" Error: Transaction already active: first Use 'jk commit' or 'jk rollback' first. ----- +``` -=== No Nested Transactions +### No Nested Transactions -JanusKey doesn't support nested transactions. For complex workflows, commit or rollback the current transaction first. +JanusKey doesn’t support nested transactions. For complex workflows, commit or rollback the current transaction first. -=== Operations Outside Transactions +### Operations Outside Transactions Operations performed without an active transaction are immediately recorded and can be individually undone: -[source,bash] ----- +``` bash # No transaction active jk delete file1.txt # Immediately recorded jk delete file2.txt # Separately recorded jk undo # Restores file2.txt only jk undo # Restores file1.txt ----- +``` -== Advanced Patterns +## Advanced Patterns -=== Batch Operations +### Batch Operations Group related changes: -[source,bash] ----- +``` bash jk begin "migrate config format" for file in config/*.json; do jk modify "$file" --sed 's/"debug": true/"debug": false/g' done jk commit ----- +``` -=== Safe Experimentation +### Safe Experimentation Try changes without risk: -[source,bash] ----- +``` bash jk begin "experimental changes" # Make changes... jk modify main.py --file experimental_main.py @@ -187,12 +164,11 @@ else jk commit echo "Tests passed, changes committed" fi ----- +``` -=== Deployment Preparation +### Deployment Preparation -[source,bash] ----- +``` bash jk begin "prepare release" # Remove development files @@ -207,14 +183,13 @@ jk preview # If everything looks good jk commit ----- +``` -=== Recovery Checkpoint +### Recovery Checkpoint Create a safe point before risky operations: -[source,bash] ----- +``` bash jk begin "risky refactor" # If something goes wrong at any point: @@ -230,14 +205,13 @@ jk modify src/main.py --file new_main.py # Finalize jk commit ----- +``` -== Transaction Lifecycle API +## Transaction Lifecycle API For programmatic use: -[source,rust] ----- +``` rust use januskey::{JanusKey, FileOperation}; fn main() -> Result<()> { @@ -276,77 +250,79 @@ fn main() -> Result<()> { Ok(()) } ----- +``` -== Formal Guarantees +## Formal Guarantees -=== Atomicity +### Atomicity -All operations in a transaction either: -* All succeed (commit) -* All are reversed (rollback) +All operations in a transaction either: \* All succeed (commit) \* All are reversed (rollback) There is no partial state. -=== Sequential Reversibility (Theorem 3.4) +### Sequential Reversibility (Theorem 3.4) -For operations stem:[O_1, O_2, \ldots, O_n] in a transaction: +For operations $O_1, O_2, \ldots, O_n$ in a transaction: -[stem] -++++ -\text{rollback}() = O_n^{-1} \circ O_{n-1}^{-1} \circ \cdots \circ O_1^{-1} -++++ +$$\text{rollback}() = O_n^{-1} \circ O_{n-1}^{-1} \circ \cdots \circ O_1^{-1}$$ Operations are undone in reverse order. -=== Isolation +### Isolation -Transaction operations are visible immediately to the file system. JanusKey doesn't provide isolation between concurrent processes—it tracks operations for reversal, not database-style ACID isolation. +Transaction operations are visible immediately to the file system. JanusKey doesn’t provide isolation between concurrent processes—it tracks operations for reversal, not database-style ACID isolation. -== Error Handling +## Error Handling -=== Operation Failure During Transaction +### Operation Failure During Transaction If an operation fails mid-transaction: -[source,bash] ----- +``` bash jk begin "multi-step" jk delete file1.txt # Success jk delete nonexistent.txt # Fails # Transaction remains active # Choose: rollback to undo file1.txt deletion, or continue ----- +``` -=== Rollback Failure +### Rollback Failure If rollback fails (e.g., content store corrupted): -[source,bash] ----- +``` bash $ jk rollback Error: Failed to restore file1.txt: Content integrity error Partial rollback completed. Manual intervention required. See: jk history --transaction current ----- +``` + +## Best Practices + +1. **Name your transactions**: Makes history more readable + +2. **Keep transactions focused**: One logical change per transaction + +3. **Preview before commit**: Always `jk preview` for complex transactions + +4. **Test after rollback**: Verify state is as expected + +5. **Don’t leave transactions open**: Commit or rollback promptly + +## Limitations + +1. **No concurrent transactions**: One active transaction per directory + +2. **No cross-directory transactions**: Each .januskey is independent -== Best Practices +3. **Operations are immediate**: File changes happen immediately, even in transaction -1. **Name your transactions**: Makes history more readable -2. **Keep transactions focused**: One logical change per transaction -3. **Preview before commit**: Always `jk preview` for complex transactions -4. **Test after rollback**: Verify state is as expected -5. **Don't leave transactions open**: Commit or rollback promptly +4. **No isolation**: Other processes see changes immediately -== Limitations +## Further Reading -1. **No concurrent transactions**: One active transaction per directory -2. **No cross-directory transactions**: Each .januskey is independent -3. **Operations are immediate**: File changes happen immediately, even in transaction -4. **No isolation**: Other processes see changes immediately +- [CLI Commands](cli.md) -== Further Reading +- [Operations Reference](../operations/index.md) -* link:cli.adoc[CLI Commands] -* link:../operations/index.adoc[Operations Reference] -* link:../theory/rmr-primitive.adoc[RMR Primitive] +- [RMR Primitive](../theory/rmr-primitive.md) diff --git a/docs/wiki/operations/index.adoc b/docs/wiki/operations/index.adoc deleted file mode 100644 index 0681167..0000000 --- a/docs/wiki/operations/index.adoc +++ /dev/null @@ -1,598 +0,0 @@ -// SPDX-License-Identifier: CC-BY-SA-4.0 -// Copyright (c) Jonathan D.A. Jewell -= Operations Reference -:toc: left -:toclevels: 3 -:icons: font -:source-highlighter: rouge - -== Overview - -JanusKey provides a comprehensive set of reversible file operations. Each operation captures sufficient metadata before execution to guarantee perfect reversal. - -== Operation Categories - -=== Core Operations - -|=== -| Operation | Description | Reversal - -| Delete -| Remove a file -| Restore from content store - -| Modify -| Change file content -| Restore original content - -| Move -| Rename/relocate file -| Move back to original path - -| Copy -| Duplicate a file -| Delete the copy - -| Create -| Create new file -| Delete the created file -|=== - -=== Extended Operations - -|=== -| Operation | Description | Reversal - -| Mkdir -| Create directory -| Remove directory - -| Rmdir -| Remove empty directory -| Recreate directory - -| RmdirRecursive -| Remove directory with contents -| Restore all contents - -| Symlink -| Create symbolic link -| Remove link - -| Append -| Add content to file -| Truncate to original size - -| Truncate -| Reduce file size -| Restore original content - -| Touch -| Update timestamps / create empty -| Restore timestamps / delete - -| Chmod -| Change permissions (Unix) -| Restore original permissions -|=== - -== Detailed Operation Reference - -=== Delete - -Removes a file while preserving its complete content and metadata for reversal. - -==== Syntax - -[source,bash] ----- -jk delete ... -jk delete "*.log" # Glob pattern -jk delete -f important.txt # Force (no confirmation) ----- - -==== Behavior - -1. Read file content -2. Capture file metadata (permissions, timestamps, etc.) -3. Store content in content store (SHA256 addressed) -4. Record operation metadata -5. Remove the file - -==== Metadata Captured - -[source,json] ----- -{ - "id": "uuid-v4", - "op_type": "DELETE", - "timestamp": "2025-12-17T10:30:00Z", - "user": "username", - "path": "/path/to/file.txt", - "content_hash": "sha256:abc123...", - "original_metadata": { - "permissions": 420, - "owner": "1000", - "group": "1000", - "size": 1234, - "modified": "2025-12-17T09:00:00Z" - } -} ----- - -==== Reversal - -[source,bash] ----- -jk undo # Restores the file with original content and metadata ----- - -=== Modify - -Changes the content of an existing file. - -==== Syntax - -[source,bash] ----- -jk modify --content "new content" -jk modify --file new_content.txt -jk modify --sed "s/old/new/g" ----- - -==== Behavior - -1. Read original content -2. Store original content in content store -3. Record operation with both content hashes -4. Write new content to file - -==== Metadata Captured - -[source,json] ----- -{ - "op_type": "MODIFY", - "path": "/path/to/file.txt", - "content_hash": "sha256:original...", - "new_content_hash": "sha256:newcontent...", - "original_metadata": { ... } -} ----- - -==== Reversal - -Restores original content: - -[source,bash] ----- -jk undo ----- - -=== Move - -Relocates a file from one path to another. - -==== Syntax - -[source,bash] ----- -jk move -jk mv # Alias ----- - -==== Behavior - -1. Verify source exists and destination doesn't -2. Create destination parent directories if needed -3. Capture source metadata -4. Perform the rename/move -5. Record operation - -==== Metadata Captured - -[source,json] ----- -{ - "op_type": "MOVE", - "path": "/original/path/file.txt", - "path_secondary": "/new/path/file.txt", - "original_metadata": { ... } -} ----- - -==== Reversal - -Moves file back: - -[source,bash] ----- -jk undo # Moves /new/path/file.txt back to /original/path/file.txt ----- - -=== Copy - -Creates a duplicate of a file. - -==== Syntax - -[source,bash] ----- -jk copy -jk cp # Alias ----- - -==== Behavior - -1. Verify source exists and destination doesn't -2. Create destination parent directories if needed -3. Copy the file -4. Record operation - -==== Metadata Captured - -[source,json] ----- -{ - "op_type": "COPY", - "path": "/source/file.txt", - "path_secondary": "/destination/file.txt" -} ----- - -==== Reversal - -Deletes the copy (source unchanged): - -[source,bash] ----- -jk undo # Deletes /destination/file.txt ----- - -=== Mkdir - -Creates a directory. - -==== Syntax - -[source,bash] ----- -jk mkdir -jk mkdir -p # Create parents ----- - -==== Behavior - -1. Verify path doesn't exist -2. Create directory (and parents if `-p`) -3. Record operation - -==== Metadata Captured - -[source,json] ----- -{ - "op_type": "MKDIR", - "path": "/path/to/directory" -} ----- - -==== Reversal - -Removes the directory: - -[source,bash] ----- -jk undo # Removes directory (must be empty) ----- - -=== Rmdir - -Removes an empty directory. - -==== Syntax - -[source,bash] ----- -jk rmdir ----- - -==== Behavior - -1. Verify directory exists and is empty -2. Capture directory metadata -3. Remove directory -4. Record operation - -==== Reversal - -Recreates the directory: - -[source,bash] ----- -jk undo ----- - -=== RmdirRecursive - -Removes a directory and all its contents. - -==== Syntax - -[source,bash] ----- -jk rmdir -r -jk rmdir --recursive ----- - -==== Behavior - -1. Walk directory tree -2. Store all file contents in content store -3. Create manifest of all files with their content hashes -4. Store manifest -5. Remove entire directory tree -6. Record operation with manifest hash - -==== Metadata Captured - -[source,json] ----- -{ - "op_type": "RMDIR", - "path": "/path/to/directory", - "content_hash": "sha256:manifest...", // Manifest of all files - "original_metadata": { ... } -} ----- - -==== Reversal - -Recreates entire directory structure with all contents: - -[source,bash] ----- -jk undo ----- - -=== Symlink - -Creates a symbolic link (Unix only). - -==== Syntax - -[source,bash] ----- -jk symlink -jk ln -s # Alias ----- - -==== Behavior - -1. Verify link path doesn't exist -2. Create symbolic link pointing to target -3. Record operation - -==== Metadata Captured - -[source,json] ----- -{ - "op_type": "SYMLINK", - "path": "/path/to/link", - "path_secondary": "/path/to/target" -} ----- - -==== Reversal - -Removes the symbolic link (target unchanged): - -[source,bash] ----- -jk undo ----- - -=== Append - -Adds content to the end of a file. - -==== Syntax - -[source,bash] ----- -jk append --content "additional content" -jk append --file content_to_add.txt ----- - -==== Behavior - -1. Verify file exists -2. Capture original file size -3. Store appended content -4. Append content to file -5. Record operation - -==== Metadata Captured - -[source,json] ----- -{ - "op_type": "APPEND", - "path": "/path/to/file.txt", - "content_hash": "sha256:appended...", - "original_metadata": { - "size": 1000 // Original size for truncation on undo - } -} ----- - -==== Reversal - -Truncates file to original size: - -[source,bash] ----- -jk undo # File returns to original size, appended content removed ----- - -=== Truncate - -Reduces a file to a specified size. - -==== Syntax - -[source,bash] ----- -jk truncate --size -jk truncate -s 100 ----- - -==== Behavior - -1. Verify file exists -2. Store original content -3. Truncate file to new size -4. Record operation - -==== Metadata Captured - -[source,json] ----- -{ - "op_type": "TRUNCATE", - "path": "/path/to/file.txt", - "content_hash": "sha256:original...", - "original_metadata": { - "size": 5000 // Original size - } -} ----- - -==== Reversal - -Restores original content: - -[source,bash] ----- -jk undo # File restored to original content ----- - -=== Touch - -Updates file timestamps or creates an empty file. - -==== Syntax - -[source,bash] ----- -jk touch # Update timestamps (or create) -jk touch --no-create # Update only, don't create ----- - -==== Behavior - -If file exists: -1. Capture original timestamps -2. Update modification time to now -3. Record operation - -If file doesn't exist and creation allowed: -1. Create empty file -2. Record operation (no original metadata) - -==== Metadata Captured - -[source,json] ----- -{ - "op_type": "TOUCH", - "path": "/path/to/file.txt", - "original_metadata": { - "modified": "2025-12-17T09:00:00Z" // Only if file existed - } -} ----- - -==== Reversal - -* If file existed: restores original timestamps -* If file was created: deletes the file - -=== Chmod (Unix) - -Changes file permissions. - -==== Syntax - -[source,bash] ----- -jk chmod 644 -jk chmod +x ----- - -==== Behavior - -1. Verify file exists -2. Capture original permissions -3. Apply new permissions -4. Record operation - -==== Metadata Captured - -[source,json] ----- -{ - "op_type": "CHMOD", - "path": "/path/to/file.txt", - "original_metadata": { - "permissions": 420 // 0o644 - }, - "new_metadata": { - "permissions": 493 // 0o755 - } -} ----- - -==== Reversal - -Restores original permissions: - -[source,bash] ----- -jk undo ----- - -== Error Handling - -All operations may return these errors: - -|=== -| Error | Cause | Resolution - -| FileNotFound -| File doesn't exist -| Check path spelling - -| PathExists -| Destination already exists -| Use different destination or delete first - -| DirectoryNotFound -| Directory doesn't exist -| Create parent directories - -| ContentIntegrityError -| Stored content corrupted -| Check disk health - -| OperationFailed -| Generic operation failure -| Check error message for details -|=== - -== Further Reading - -* link:../guides/cli.adoc[CLI Commands Guide] -* link:../guides/transactions.adoc[Transactions Guide] -* link:../theory/formal-model.adoc[Formal Model] diff --git a/docs/wiki/operations/index.md b/docs/wiki/operations/index.md new file mode 100644 index 0000000..4952ad3 --- /dev/null +++ b/docs/wiki/operations/index.md @@ -0,0 +1,538 @@ + + + +# Operations Reference + +## Overview + +JanusKey provides a comprehensive set of reversible file operations. Each operation captures sufficient metadata before execution to guarantee perfect reversal. + +## Operation Categories + +### Core Operations + +| Operation | Description | Reversal | +|-----------|----------------------|----------------------------| +| Delete | Remove a file | Restore from content store | +| Modify | Change file content | Restore original content | +| Move | Rename/relocate file | Move back to original path | +| Copy | Duplicate a file | Delete the copy | +| Create | Create new file | Delete the created file | + +### Extended Operations + +| Operation | Description | Reversal | +|----------------|----------------------------------|------------------------------| +| Mkdir | Create directory | Remove directory | +| Rmdir | Remove empty directory | Recreate directory | +| RmdirRecursive | Remove directory with contents | Restore all contents | +| Symlink | Create symbolic link | Remove link | +| Append | Add content to file | Truncate to original size | +| Truncate | Reduce file size | Restore original content | +| Touch | Update timestamps / create empty | Restore timestamps / delete | +| Chmod | Change permissions (Unix) | Restore original permissions | + +## Detailed Operation Reference + +### Delete + +Removes a file while preserving its complete content and metadata for reversal. + +#### Syntax + +``` bash +jk delete ... +jk delete "*.log" # Glob pattern +jk delete -f important.txt # Force (no confirmation) +``` + +#### Behavior + +1. Read file content + +2. Capture file metadata (permissions, timestamps, etc.) + +3. Store content in content store (SHA256 addressed) + +4. Record operation metadata + +5. Remove the file + +#### Metadata Captured + +``` json +{ + "id": "uuid-v4", + "op_type": "DELETE", + "timestamp": "2025-12-17T10:30:00Z", + "user": "username", + "path": "/path/to/file.txt", + "content_hash": "sha256:abc123...", + "original_metadata": { + "permissions": 420, + "owner": "1000", + "group": "1000", + "size": 1234, + "modified": "2025-12-17T09:00:00Z" + } +} +``` + +#### Reversal + +``` bash +jk undo # Restores the file with original content and metadata +``` + +### Modify + +Changes the content of an existing file. + +#### Syntax + +``` bash +jk modify --content "new content" +jk modify --file new_content.txt +jk modify --sed "s/old/new/g" +``` + +#### Behavior + +1. Read original content + +2. Store original content in content store + +3. Record operation with both content hashes + +4. Write new content to file + +#### Metadata Captured + +``` json +{ + "op_type": "MODIFY", + "path": "/path/to/file.txt", + "content_hash": "sha256:original...", + "new_content_hash": "sha256:newcontent...", + "original_metadata": { ... } +} +``` + +#### Reversal + +Restores original content: + +``` bash +jk undo +``` + +### Move + +Relocates a file from one path to another. + +#### Syntax + +``` bash +jk move +jk mv # Alias +``` + +#### Behavior + +1. Verify source exists and destination doesn’t + +2. Create destination parent directories if needed + +3. Capture source metadata + +4. Perform the rename/move + +5. Record operation + +#### Metadata Captured + +``` json +{ + "op_type": "MOVE", + "path": "/original/path/file.txt", + "path_secondary": "/new/path/file.txt", + "original_metadata": { ... } +} +``` + +#### Reversal + +Moves file back: + +``` bash +jk undo # Moves /new/path/file.txt back to /original/path/file.txt +``` + +### Copy + +Creates a duplicate of a file. + +#### Syntax + +``` bash +jk copy +jk cp # Alias +``` + +#### Behavior + +1. Verify source exists and destination doesn’t + +2. Create destination parent directories if needed + +3. Copy the file + +4. Record operation + +#### Metadata Captured + +``` json +{ + "op_type": "COPY", + "path": "/source/file.txt", + "path_secondary": "/destination/file.txt" +} +``` + +#### Reversal + +Deletes the copy (source unchanged): + +``` bash +jk undo # Deletes /destination/file.txt +``` + +### Mkdir + +Creates a directory. + +#### Syntax + +``` bash +jk mkdir +jk mkdir -p # Create parents +``` + +#### Behavior + +1. Verify path doesn’t exist + +2. Create directory (and parents if `-p`) + +3. Record operation + +#### Metadata Captured + +``` json +{ + "op_type": "MKDIR", + "path": "/path/to/directory" +} +``` + +#### Reversal + +Removes the directory: + +``` bash +jk undo # Removes directory (must be empty) +``` + +### Rmdir + +Removes an empty directory. + +#### Syntax + +``` bash +jk rmdir +``` + +#### Behavior + +1. Verify directory exists and is empty + +2. Capture directory metadata + +3. Remove directory + +4. Record operation + +#### Reversal + +Recreates the directory: + +``` bash +jk undo +``` + +### RmdirRecursive + +Removes a directory and all its contents. + +#### Syntax + +``` bash +jk rmdir -r +jk rmdir --recursive +``` + +#### Behavior + +1. Walk directory tree + +2. Store all file contents in content store + +3. Create manifest of all files with their content hashes + +4. Store manifest + +5. Remove entire directory tree + +6. Record operation with manifest hash + +#### Metadata Captured + +``` json +{ + "op_type": "RMDIR", + "path": "/path/to/directory", + "content_hash": "sha256:manifest...", // Manifest of all files + "original_metadata": { ... } +} +``` + +#### Reversal + +Recreates entire directory structure with all contents: + +``` bash +jk undo +``` + +### Symlink + +Creates a symbolic link (Unix only). + +#### Syntax + +``` bash +jk symlink +jk ln -s # Alias +``` + +#### Behavior + +1. Verify link path doesn’t exist + +2. Create symbolic link pointing to target + +3. Record operation + +#### Metadata Captured + +``` json +{ + "op_type": "SYMLINK", + "path": "/path/to/link", + "path_secondary": "/path/to/target" +} +``` + +#### Reversal + +Removes the symbolic link (target unchanged): + +``` bash +jk undo +``` + +### Append + +Adds content to the end of a file. + +#### Syntax + +``` bash +jk append --content "additional content" +jk append --file content_to_add.txt +``` + +#### Behavior + +1. Verify file exists + +2. Capture original file size + +3. Store appended content + +4. Append content to file + +5. Record operation + +#### Metadata Captured + +``` json +{ + "op_type": "APPEND", + "path": "/path/to/file.txt", + "content_hash": "sha256:appended...", + "original_metadata": { + "size": 1000 // Original size for truncation on undo + } +} +``` + +#### Reversal + +Truncates file to original size: + +``` bash +jk undo # File returns to original size, appended content removed +``` + +### Truncate + +Reduces a file to a specified size. + +#### Syntax + +``` bash +jk truncate --size +jk truncate -s 100 +``` + +#### Behavior + +1. Verify file exists + +2. Store original content + +3. Truncate file to new size + +4. Record operation + +#### Metadata Captured + +``` json +{ + "op_type": "TRUNCATE", + "path": "/path/to/file.txt", + "content_hash": "sha256:original...", + "original_metadata": { + "size": 5000 // Original size + } +} +``` + +#### Reversal + +Restores original content: + +``` bash +jk undo # File restored to original content +``` + +### Touch + +Updates file timestamps or creates an empty file. + +#### Syntax + +``` bash +jk touch # Update timestamps (or create) +jk touch --no-create # Update only, don't create +``` + +#### Behavior + +If file exists: 1. Capture original timestamps 2. Update modification time to now 3. Record operation + +If file doesn’t exist and creation allowed: 1. Create empty file 2. Record operation (no original metadata) + +#### Metadata Captured + +``` json +{ + "op_type": "TOUCH", + "path": "/path/to/file.txt", + "original_metadata": { + "modified": "2025-12-17T09:00:00Z" // Only if file existed + } +} +``` + +#### Reversal + +- If file existed: restores original timestamps + +- If file was created: deletes the file + +### Chmod (Unix) + +Changes file permissions. + +#### Syntax + +``` bash +jk chmod 644 +jk chmod +x +``` + +#### Behavior + +1. Verify file exists + +2. Capture original permissions + +3. Apply new permissions + +4. Record operation + +#### Metadata Captured + +``` json +{ + "op_type": "CHMOD", + "path": "/path/to/file.txt", + "original_metadata": { + "permissions": 420 // 0o644 + }, + "new_metadata": { + "permissions": 493 // 0o755 + } +} +``` + +#### Reversal + +Restores original permissions: + +``` bash +jk undo +``` + +## Error Handling + +All operations may return these errors: + +| Error | Cause | Resolution | +|-----------------------|----------------------------|-------------------------------------------| +| FileNotFound | File doesn’t exist | Check path spelling | +| PathExists | Destination already exists | Use different destination or delete first | +| DirectoryNotFound | Directory doesn’t exist | Create parent directories | +| ContentIntegrityError | Stored content corrupted | Check disk health | +| OperationFailed | Generic operation failure | Check error message for details | + +## Further Reading + +- [CLI Commands Guide](../guides/cli.md) + +- [Transactions Guide](../guides/transactions.md) + +- [Formal Model](../theory/formal-model.md) diff --git a/docs/wiki/theory/formal-model.adoc b/docs/wiki/theory/formal-model.adoc deleted file mode 100644 index 2afe2d9..0000000 --- a/docs/wiki/theory/formal-model.adoc +++ /dev/null @@ -1,351 +0,0 @@ -// SPDX-License-Identifier: CC-BY-SA-4.0 -// Copyright (c) Jonathan D.A. Jewell -= Formal Model of File System State -:toc: left -:toclevels: 3 -:icons: font -:source-highlighter: rouge -:stem: latexmath - -== Introduction - -This document defines the formal model underlying JanusKey's reversibility guarantees. We establish mathematical definitions for file system state, operations, and the conditions for reversibility. - -== Definitions - -=== File System State - -A file system state stem:[S] is a partial function: - -[stem] -++++ -S: \mathcal{P} \rightharpoonup \mathcal{F} -++++ - -where: - -* stem:[\mathcal{P}] is the set of all valid file paths -* stem:[\mathcal{F}] is the set of all file states -* stem:[\rightharpoonup] denotes a partial function (not all paths have files) - -=== File State - -A file state stem:[f \in \mathcal{F}] is a tuple: - -[stem] -++++ -f = (c, m) -++++ - -where: - -* stem:[c \in \mathcal{C}] is the content (byte sequence) -* stem:[m \in \mathcal{M}] is the metadata - -=== Metadata - -Metadata stem:[m \in \mathcal{M}] is a tuple: - -[stem] -++++ -m = (\pi, o, g, s, t, \ell) -++++ - -where: - -* stem:[\pi \in \{0, \ldots, 0o7777\}] is the permission mode -* stem:[o \in \mathbb{N}] is the owner uid -* stem:[g \in \mathbb{N}] is the group gid -* stem:[s \in \mathbb{N}] is the file size -* stem:[t \in \mathbb{R}] is the modification timestamp -* stem:[\ell \in \mathcal{P} \cup \{\bot\}] is the symlink target (or stem:[\bot] if not a symlink) - -=== Content Hash - -The content hash function stem:[H: \mathcal{C} \to \mathcal{H}] satisfies: - -[stem] -++++ -\forall c_1, c_2 \in \mathcal{C}: H(c_1) = H(c_2) \Rightarrow c_1 = c_2 \text{ (with overwhelming probability)} -++++ - -JanusKey uses SHA256 for stem:[H]. - -== Operations - -=== Operation Definition - -An operation stem:[O] is a function: - -[stem] -++++ -O: \mathcal{S} \to \mathcal{S} -++++ - -that transforms one file system state to another. - -=== Reversible Operation - -An operation stem:[O] is **reversible** if there exists an inverse operation stem:[O^{-1}] such that: - -[stem] -++++ -\forall S \in \mathcal{S}: O^{-1}(O(S)) = S -++++ - -=== Operation Catalog - -==== Delete - -[stem] -++++ -\text{DELETE}_p: S \mapsto S' \text{ where } S'(q) = \begin{cases} \bot & \text{if } q = p \\ S(q) & \text{otherwise} \end{cases} -++++ - -**Precondition**: stem:[S(p) \neq \bot] - -**Inverse**: stem:[\text{CREATE}_p] with stored content and metadata - -==== Create - -[stem] -++++ -\text{CREATE}_{p,c,m}: S \mapsto S' \text{ where } S'(q) = \begin{cases} (c, m) & \text{if } q = p \\ S(q) & \text{otherwise} \end{cases} -++++ - -**Precondition**: stem:[S(p) = \bot] - -**Inverse**: stem:[\text{DELETE}_p] - -==== Modify - -[stem] -++++ -\text{MODIFY}_{p,c'}: S \mapsto S' \text{ where } S'(q) = \begin{cases} (c', m) & \text{if } q = p \land S(p) = (c, m) \\ S(q) & \text{otherwise} \end{cases} -++++ - -**Precondition**: stem:[S(p) \neq \bot] - -**Inverse**: stem:[\text{MODIFY}_{p,c}] with original content stem:[c] - -==== Move - -[stem] -++++ -\text{MOVE}_{p,q}: S \mapsto S' \text{ where } S'(r) = \begin{cases} \bot & \text{if } r = p \\ S(p) & \text{if } r = q \\ S(r) & \text{otherwise} \end{cases} -++++ - -**Precondition**: stem:[S(p) \neq \bot \land S(q) = \bot] - -**Inverse**: stem:[\text{MOVE}_{q,p}] - -==== Copy - -[stem] -++++ -\text{COPY}_{p,q}: S \mapsto S' \text{ where } S'(r) = \begin{cases} S(p) & \text{if } r = q \\ S(r) & \text{otherwise} \end{cases} -++++ - -**Precondition**: stem:[S(p) \neq \bot \land S(q) = \bot] - -**Inverse**: stem:[\text{DELETE}_q] - -==== Append - -[stem] -++++ -\text{APPEND}_{p,c'}: S \mapsto S' \text{ where } S'(q) = \begin{cases} (c \cdot c', m') & \text{if } q = p \land S(p) = (c, m) \\ S(q) & \text{otherwise} \end{cases} -++++ - -where stem:[c \cdot c'] denotes concatenation and stem:[m'] updates size. - -**Precondition**: stem:[S(p) \neq \bot] - -**Inverse**: stem:[\text{TRUNCATE}_{p,|c|}] to original size - -==== Truncate - -[stem] -++++ -\text{TRUNCATE}_{p,n}: S \mapsto S' \text{ where } S'(q) = \begin{cases} (c[0:n], m') & \text{if } q = p \land S(p) = (c, m) \\ S(q) & \text{otherwise} \end{cases} -++++ - -**Precondition**: stem:[S(p) \neq \bot] - -**Inverse**: stem:[\text{MODIFY}_{p,c}] with original content - -== Theorems - -=== Theorem 1: Individual Reversibility - -Every JanusKey operation is reversible. - -==== Proof - -We show for each operation type: - -|=== -| Operation | Inverse | Reversibility Condition - -| DELETE -| CREATE with stored (c,m) -| Content and metadata stored before delete - -| CREATE -| DELETE -| No information loss (creation is addition) - -| MODIFY -| MODIFY with original c -| Original content stored - -| MOVE -| MOVE in reverse direction -| Source and destination paths stored - -| COPY -| DELETE of copy -| Destination path stored - -| APPEND -| TRUNCATE to original size -| Original size stored in metadata - -| TRUNCATE -| MODIFY with original content -| Original content stored -|=== - -All inverse information is captured by `OperationMetadata`. ∎ - -=== Theorem 2: Composition Reversibility - -If stem:[O_1, O_2] are reversible operations, then stem:[O_2 \circ O_1] is reversible with inverse stem:[O_1^{-1} \circ O_2^{-1}]. - -==== Proof - -Let stem:[S] be the initial state. - -[stem] -++++ -(O_1^{-1} \circ O_2^{-1})(O_2 \circ O_1)(S) = O_1^{-1}(O_2^{-1}(O_2(O_1(S)))) -++++ - -By reversibility of stem:[O_2]: -[stem] -++++ -= O_1^{-1}(O_1(S)) -++++ - -By reversibility of stem:[O_1]: -[stem] -++++ -= S -++++ - -∎ - -=== Theorem 3: Sequential Reversibility (Theorem 3.4) - -For any sequence of reversible operations stem:[\langle O_1, O_2, \ldots, O_n \rangle]: - -[stem] -++++ -\langle O_n^{-1}, O_{n-1}^{-1}, \ldots, O_1^{-1} \rangle \text{ reverses } \langle O_1, O_2, \ldots, O_n \rangle -++++ - -==== Proof - -By induction on stem:[n], using Theorem 2. ∎ - -=== Theorem 4: Transaction Atomicity - -A transaction stem:[T = \langle O_1, \ldots, O_n \rangle] with rollback satisfies: - -Either: - -1. **Commit**: All operations applied, all reversible -2. **Rollback**: State equals initial state - -==== Proof - -Rollback executes stem:[\langle O_n^{-1}, \ldots, O_1^{-1} \rangle]. By Theorem 3, this restores initial state. ∎ - -== Content-Addressed Storage Properties - -=== Definition: Content Store - -A content store stem:[\Sigma] is a partial function: - -[stem] -++++ -\Sigma: \mathcal{H} \rightharpoonup \mathcal{C} -++++ - -with operations: - -* stem:[\text{store}(c) = h] where stem:[h = H(c)] and stem:[\Sigma(h) := c] -* stem:[\text{retrieve}(h) = \Sigma(h)] - -=== Theorem 5: Content Integrity - -[stem] -++++ -\forall c: \text{retrieve}(\text{store}(c)) = c -++++ - -==== Proof - -By definition of store and retrieve, and the collision resistance of stem:[H]. ∎ - -=== Theorem 6: Deduplication Correctness - -[stem] -++++ -\forall c_1, c_2: H(c_1) = H(c_2) \Rightarrow \text{store}(c_1) = \text{store}(c_2) -++++ - -And both can be retrieved correctly. - -==== Proof - -If stem:[H(c_1) = H(c_2)], then by collision resistance, stem:[c_1 = c_2]. Storage of either produces the same stem:[h], and retrieval returns the identical content. ∎ - -== Invariants - -=== Invariant 1: Metadata Completeness - -For every operation stem:[O] recorded in the metadata store: - -[stem] -++++ -\text{sufficient}(\text{metadata}(O), O^{-1}) -++++ - -The metadata contains all information needed to compute the inverse. - -=== Invariant 2: Content Availability - -For every content hash stem:[h] referenced in operation metadata: - -[stem] -++++ -\Sigma(h) \neq \bot -++++ - -Referenced content is always available in the content store. - -=== Invariant 3: Operation Ordering - -The metadata store maintains temporal ordering: - -[stem] -++++ -\forall O_i, O_j: i < j \Rightarrow \text{timestamp}(O_i) \leq \text{timestamp}(O_j) -++++ - -== Further Reading - -* link:reversibility-theory.adoc[Extended Reversibility Theory] -* link:rmr-primitive.adoc[RMR Primitive Specification] -* link:../formal-proofs/index.adoc[Complete Formal Proofs] diff --git a/docs/wiki/theory/formal-model.md b/docs/wiki/theory/formal-model.md new file mode 100644 index 0000000..27c1a3a --- /dev/null +++ b/docs/wiki/theory/formal-model.md @@ -0,0 +1,270 @@ + + + +# Formal Model of File System State + +## Introduction + +This document defines the formal model underlying JanusKey’s reversibility guarantees. We establish mathematical definitions for file system state, operations, and the conditions for reversibility. + +## Definitions + +### File System State + +A file system state $S$ is a partial function: + +$$S: \mathcal{P} \rightharpoonup \mathcal{F}$$ + +where: + +- $\mathcal{P}$ is the set of all valid file paths + +- $\mathcal{F}$ is the set of all file states + +- $\rightharpoonup$ denotes a partial function (not all paths have files) + +### File State + +A file state $f \in \mathcal{F}$ is a tuple: + +$$f = (c, m)$$ + +where: + +- $c \in \mathcal{C}$ is the content (byte sequence) + +- $m \in \mathcal{M}$ is the metadata + +### Metadata + +Metadata $m \in \mathcal{M}$ is a tuple: + +$$m = (\pi, o, g, s, t, \ell)$$ + +where: + +- $\pi \in \{0, \ldots, 0o7777\}$ is the permission mode + +- $o \in \mathbb{N}$ is the owner uid + +- $g \in \mathbb{N}$ is the group gid + +- $s \in \mathbb{N}$ is the file size + +- $t \in \mathbb{R}$ is the modification timestamp + +- $\ell \in \mathcal{P} \cup \{\bot\}$ is the symlink target (or $\bot$ if not a symlink) + +### Content Hash + +The content hash function $H: \mathcal{C} \to \mathcal{H}$ satisfies: + +$$\forall c_1, c_2 \in \mathcal{C}: H(c_1) = H(c_2) \Rightarrow c_1 = c_2 \text{ (with overwhelming probability)}$$ + +JanusKey uses SHA256 for $H$. + +## Operations + +### Operation Definition + +An operation $O$ is a function: + +$$O: \mathcal{S} \to \mathcal{S}$$ + +that transforms one file system state to another. + +### Reversible Operation + +An operation $O$ is **reversible** if there exists an inverse operation $O^{-1}$ such that: + +$$\forall S \in \mathcal{S}: O^{-1}(O(S)) = S$$ + +### Operation Catalog + +#### Delete + +$$\text{DELETE}_p: S \mapsto S' \text{ where } S'(q) = \begin{cases} \bot & \text{if } q = p \\ S(q) & \text{otherwise} \end{cases}$$ + +**Precondition**: $S(p) \neq \bot$ + +**Inverse**: $\text{CREATE}_p$ with stored content and metadata + +#### Create + +$$\text{CREATE}_{p,c,m}: S \mapsto S' \text{ where } S'(q) = \begin{cases} (c, m) & \text{if } q = p \\ S(q) & \text{otherwise} \end{cases}$$ + +**Precondition**: $S(p) = \bot$ + +**Inverse**: $\text{DELETE}_p$ + +#### Modify + +$$\text{MODIFY}_{p,c'}: S \mapsto S' \text{ where } S'(q) = \begin{cases} (c', m) & \text{if } q = p \land S(p) = (c, m) \\ S(q) & \text{otherwise} \end{cases}$$ + +**Precondition**: $S(p) \neq \bot$ + +**Inverse**: $\text{MODIFY}_{p,c}$ with original content $c$ + +#### Move + +$$\text{MOVE}_{p,q}: S \mapsto S' \text{ where } S'(r) = \begin{cases} \bot & \text{if } r = p \\ S(p) & \text{if } r = q \\ S(r) & \text{otherwise} \end{cases}$$ + +**Precondition**: $S(p) \neq \bot \land S(q) = \bot$ + +**Inverse**: $\text{MOVE}_{q,p}$ + +#### Copy + +$$\text{COPY}_{p,q}: S \mapsto S' \text{ where } S'(r) = \begin{cases} S(p) & \text{if } r = q \\ S(r) & \text{otherwise} \end{cases}$$ + +**Precondition**: $S(p) \neq \bot \land S(q) = \bot$ + +**Inverse**: $\text{DELETE}_q$ + +#### Append + +$$\text{APPEND}_{p,c'}: S \mapsto S' \text{ where } S'(q) = \begin{cases} (c \cdot c', m') & \text{if } q = p \land S(p) = (c, m) \\ S(q) & \text{otherwise} \end{cases}$$ + +where $c \cdot c'$ denotes concatenation and $m'$ updates size. + +**Precondition**: $S(p) \neq \bot$ + +**Inverse**: $\text{TRUNCATE}_{p,|c|}$ to original size + +#### Truncate + +$$\text{TRUNCATE}_{p,n}: S \mapsto S' \text{ where } S'(q) = \begin{cases} (c[0:n], m') & \text{if } q = p \land S(p) = (c, m) \\ S(q) & \text{otherwise} \end{cases}$$ + +**Precondition**: $S(p) \neq \bot$ + +**Inverse**: $\text{MODIFY}_{p,c}$ with original content + +## Theorems + +### Theorem 1: Individual Reversibility + +Every JanusKey operation is reversible. + +#### Proof + +We show for each operation type: + +| Operation | Inverse | Reversibility Condition | +|-----------|------------------------------|--------------------------------------------| +| DELETE | CREATE with stored (c,m) | Content and metadata stored before delete | +| CREATE | DELETE | No information loss (creation is addition) | +| MODIFY | MODIFY with original c | Original content stored | +| MOVE | MOVE in reverse direction | Source and destination paths stored | +| COPY | DELETE of copy | Destination path stored | +| APPEND | TRUNCATE to original size | Original size stored in metadata | +| TRUNCATE | MODIFY with original content | Original content stored | + +All inverse information is captured by `OperationMetadata`. ∎ + +### Theorem 2: Composition Reversibility + +If $O_1, O_2$ are reversible operations, then $O_2 \circ O_1$ is reversible with inverse $O_1^{-1} \circ O_2^{-1}$. + +#### Proof + +Let $S$ be the initial state. + +$$(O_1^{-1} \circ O_2^{-1})(O_2 \circ O_1)(S) = O_1^{-1}(O_2^{-1}(O_2(O_1(S))))$$ + +By reversibility of $O_2$: + +$$= O_1^{-1}(O_1(S))$$ + +By reversibility of $O_1$: + +$$= S$$ + +∎ + +### Theorem 3: Sequential Reversibility (Theorem 3.4) + +For any sequence of reversible operations $\langle O_1, O_2, \ldots, O_n \rangle$: + +$$\langle O_n^{-1}, O_{n-1}^{-1}, \ldots, O_1^{-1} \rangle \text{ reverses } \langle O_1, O_2, \ldots, O_n \rangle$$ + +#### Proof + +By induction on $n$, using Theorem 2. ∎ + +### Theorem 4: Transaction Atomicity + +A transaction $T = \langle O_1, \ldots, O_n \rangle$ with rollback satisfies: + +Either: + +1. **Commit**: All operations applied, all reversible + +2. **Rollback**: State equals initial state + +#### Proof + +Rollback executes $\langle O_n^{-1}, \ldots, O_1^{-1} \rangle$. By Theorem 3, this restores initial state. ∎ + +## Content-Addressed Storage Properties + +### Definition: Content Store + +A content store $\Sigma$ is a partial function: + +$$\Sigma: \mathcal{H} \rightharpoonup \mathcal{C}$$ + +with operations: + +- $\text{store}(c) = h$ where $h = H(c)$ and $\Sigma(h) := c$ + +- $\text{retrieve}(h) = \Sigma(h)$ + +### Theorem 5: Content Integrity + +$$\forall c: \text{retrieve}(\text{store}(c)) = c$$ + +#### Proof + +By definition of store and retrieve, and the collision resistance of $H$. ∎ + +### Theorem 6: Deduplication Correctness + +$$\forall c_1, c_2: H(c_1) = H(c_2) \Rightarrow \text{store}(c_1) = \text{store}(c_2)$$ + +And both can be retrieved correctly. + +#### Proof + +If $H(c_1) = H(c_2)$, then by collision resistance, $c_1 = c_2$. Storage of either produces the same $h$, and retrieval returns the identical content. ∎ + +## Invariants + +### Invariant 1: Metadata Completeness + +For every operation $O$ recorded in the metadata store: + +$$\text{sufficient}(\text{metadata}(O), O^{-1})$$ + +The metadata contains all information needed to compute the inverse. + +### Invariant 2: Content Availability + +For every content hash $h$ referenced in operation metadata: + +$$\Sigma(h) \neq \bot$$ + +Referenced content is always available in the content store. + +### Invariant 3: Operation Ordering + +The metadata store maintains temporal ordering: + +$$\forall O_i, O_j: i < j \Rightarrow \text{timestamp}(O_i) \leq \text{timestamp}(O_j)$$ + +## Further Reading + +- [Extended Reversibility Theory](reversibility-theory.md) + +- [RMR Primitive Specification](rmr-primitive.md) + +- [Complete Formal Proofs](../formal-proofs/index.md) diff --git a/docs/wiki/theory/index.adoc b/docs/wiki/theory/index.adoc deleted file mode 100644 index 4b2d7a9..0000000 --- a/docs/wiki/theory/index.adoc +++ /dev/null @@ -1,133 +0,0 @@ -// SPDX-License-Identifier: CC-BY-SA-4.0 -// Copyright (c) Jonathan D.A. Jewell -= Theoretical Foundations -:toc: left -:toclevels: 3 -:icons: font -:source-highlighter: rouge -:stem: latexmath - -== Overview - -JanusKey's design is grounded in rigorous theoretical foundations that provide mathematical guarantees for reversibility. This section covers the core concepts that make JanusKey's guarantees possible. - -=== Contents - -1. link:mpr.adoc[Maximal Principle Reduction (MPR)] -2. link:maa-framework.adoc[Mutually Assured Accountability (MAA) Framework] -3. link:rmr-primitive.adoc[RMR: Reversible Transaction Primitive] -4. link:rmo-primitive.adoc[RMO: Obliterative Wipe Primitive] -5. link:formal-model.adoc[Formal Model of File System State] -6. link:reversibility-theory.adoc[Reversibility Theory] - -== Core Axiom: Maximal Principle Reduction - -The foundational principle underlying JanusKey is **Maximal Principle Reduction (MPR)**: - -[quote] -Security and correctness should be achieved by construction, not verification. The system should be designed such that vulnerabilities are structurally impossible, not merely unlikely or detectable. - -=== Traditional vs MPR Approach - -[cols="1,1,1"] -|=== -| Aspect | Traditional | MPR - -| Data safety -| Achieved through backups -| Impossible to lose by design - -| Verification -| Post-hoc testing -| Compile-time guarantees - -| Trust model -| Trust operational processes -| Trust mathematical proofs - -| Failure mode -| Graceful degradation -| No degradation possible -|=== - -== The MAA Framework - -The **Mutually Assured Accountability (MAA) Framework** balances two opposing requirements: - -1. **Auditability**: The ability to track, verify, and replay all state changes -2. **User Autonomy**: The right to permanently erase data (GDPR Article 17) - -These are implemented through dual primitives: - -* **RMR (Reversible Transaction)**: Guarantees perfect state reversal -* **RMO (Obliterative Wipe)**: Guarantees permanent, verifiable data erasure - -[ditaa] -.... - +----------------+ - | MAA Framework | - +-------+--------+ - | - +---------------+---------------+ - | | - +-------v-------+ +-------v-------+ - | RMR | | RMO | - | Reversibility | | Obliteration | - +-------+-------+ +-------+-------+ - | | - +-------v-------+ +-------v-------+ - | Auditability | | Autonomy | - | Accountability| | Privacy | - +---------------+ +---------------+ -.... - -== Mathematical Foundation - -=== File System State - -A file system state stem:[S] is defined as a total function: - -[stem] -++++ -S: \text{Path} \to \text{FileState} -++++ - -where: - -[stem] -++++ -\text{FileState} = \text{Content} \times \text{Metadata} \cup \{\bot\} -++++ - -The symbol stem:[\bot] represents a non-existent file. - -=== Operations as Functions - -Each operation stem:[O] is a function: - -[stem] -++++ -O: S \to S -++++ - -For JanusKey operations, we require the existence of an inverse: - -[stem] -++++ -\forall O \in \text{Operations}: \exists O^{-1}: O^{-1}(O(S)) = S -++++ - -== Historical Context - -JanusKey builds on foundational work in: - -* **Algorithmic Reversibility** (Bennett, 1973): Theoretical foundation for reversible computing -* **Landauer's Principle**: Thermodynamic limits of information erasure -* **ACID Properties**: Transaction guarantees in database systems -* **Content-Addressable Storage**: Git's object model for integrity - -== Next Steps - -* link:mpr.adoc[Deep dive into Maximal Principle Reduction] -* link:formal-model.adoc[Formal model definitions and proofs] -* link:../architecture/index.adoc[See how theory maps to implementation] diff --git a/docs/wiki/theory/index.md b/docs/wiki/theory/index.md new file mode 100644 index 0000000..6054a36 --- /dev/null +++ b/docs/wiki/theory/index.md @@ -0,0 +1,113 @@ + + + +# Theoretical Foundations + +## Overview + +JanusKey’s design is grounded in rigorous theoretical foundations that provide mathematical guarantees for reversibility. This section covers the core concepts that make JanusKey’s guarantees possible. + +### Contents + +1. [Maximal Principle Reduction (MPR)](mpr.md) + +2. [Mutually Assured Accountability (MAA) Framework](maa-framework.md) + +3. [RMR: Reversible Transaction Primitive](rmr-primitive.md) + +4. [RMO: Obliterative Wipe Primitive](rmo-primitive.md) + +5. [Formal Model of File System State](formal-model.md) + +6. [Reversibility Theory](reversibility-theory.md) + +## Core Axiom: Maximal Principle Reduction + +The foundational principle underlying JanusKey is **Maximal Principle Reduction (MPR)**: + +> Security and correctness should be achieved by construction, not verification. The system should be designed such that vulnerabilities are structurally impossible, not merely unlikely or detectable. + +### Traditional vs MPR Approach + +| Aspect | Traditional | MPR | +|--------------|-----------------------------|------------------------------| +| Data safety | Achieved through backups | Impossible to lose by design | +| Verification | Post-hoc testing | Compile-time guarantees | +| Trust model | Trust operational processes | Trust mathematical proofs | +| Failure mode | Graceful degradation | No degradation possible | + +## The MAA Framework + +The **Mutually Assured Accountability (MAA) Framework** balances two opposing requirements: + +1. **Auditability**: The ability to track, verify, and replay all state changes + +2. **User Autonomy**: The right to permanently erase data (GDPR Article 17) + +These are implemented through dual primitives: + +- **RMR (Reversible Transaction)**: Guarantees perfect state reversal + +- **RMO (Obliterative Wipe)**: Guarantees permanent, verifiable data erasure + + + + +----------------+ + | MAA Framework | + +-------+--------+ + | + +---------------+---------------+ + | | + +-------v-------+ +-------v-------+ + | RMR | | RMO | + | Reversibility | | Obliteration | + +-------+-------+ +-------+-------+ + | | + +-------v-------+ +-------v-------+ + | Auditability | | Autonomy | + | Accountability| | Privacy | + +---------------+ +---------------+ + +## Mathematical Foundation + +### File System State + +A file system state $S$ is defined as a total function: + +$$S: \text{Path} \to \text{FileState}$$ + +where: + +$$\text{FileState} = \text{Content} \times \text{Metadata} \cup \{\bot\}$$ + +The symbol $\bot$ represents a non-existent file. + +### Operations as Functions + +Each operation $O$ is a function: + +$$O: S \to S$$ + +For JanusKey operations, we require the existence of an inverse: + +$$\forall O \in \text{Operations}: \exists O^{-1}: O^{-1}(O(S)) = S$$ + +## Historical Context + +JanusKey builds on foundational work in: + +- **Algorithmic Reversibility** (Bennett, 1973): Theoretical foundation for reversible computing + +- **Landauer’s Principle**: Thermodynamic limits of information erasure + +- **ACID Properties**: Transaction guarantees in database systems + +- **Content-Addressable Storage**: Git’s object model for integrity + +## Next Steps + +- [Deep dive into Maximal Principle Reduction](mpr.md) + +- [Formal model definitions and proofs](formal-model.md) + +- [See how theory maps to implementation](../architecture/index.md) diff --git a/docs/wiki/theory/maa-framework.adoc b/docs/wiki/theory/maa-framework.adoc deleted file mode 100644 index b9c5456..0000000 --- a/docs/wiki/theory/maa-framework.adoc +++ /dev/null @@ -1,286 +0,0 @@ -// SPDX-License-Identifier: CC-BY-SA-4.0 -// Copyright (c) Jonathan D.A. Jewell -= Mutually Assured Accountability (MAA) Framework -:toc: left -:toclevels: 3 -:icons: font -:source-highlighter: rouge -:stem: latexmath - -== Abstract - -The MAA Framework is an ethical and engineering paradigm that structurally balances two opposing requirements in data management systems: - -1. **Accountability/Auditability**: Systems must track changes for compliance, debugging, and recovery -2. **User Autonomy/Privacy**: Users must have verifiable control over their data, including deletion - -These requirements are often in tension. MAA resolves this through formal primitives that provide mathematical guarantees for both. - -== The Problem Statement - -=== The Accountability Requirement - -Modern systems require auditability for: - -* **Regulatory compliance** (SOX, HIPAA, financial regulations) -* **Security forensics** (incident investigation) -* **System reliability** (debugging, recovery) -* **Business continuity** (disaster recovery) - -This pushes toward: _never delete anything, log everything, keep all history_. - -=== The Autonomy Requirement - -Users and regulations require data control: - -* **GDPR Article 17** (Right to Erasure) -* **CCPA** (California Consumer Privacy Act) -* **User trust** (promise of deletion must be kept) -* **Security** (compromised data should be destroyable) - -This pushes toward: _complete, verifiable deletion capability_. - -=== The Tension - -[ditaa] -.... - Accountability <---------> Autonomy - "Keep everything" "Delete completely" - - Traditional solutions: - +------------------+ or +------------------+ - | Keep all data | | Allow deletion | - | No real deletion | | No audit trail | - +------------------+ +------------------+ - - Result: Always compromise one goal -.... - -== The MAA Solution - -MAA introduces two formally verified primitives that satisfy both requirements: - -=== RMR (Reversible Transaction) - -[quote] -A primitive for state transitions that guarantees instant, mathematically certain rollback to a prior safe state via Algorithmic Reversibility. - -RMR provides: - -* Complete state capture before changes -* Guaranteed reversal capability -* Full audit trail of all operations -* Accountability without data loss risk - -See: link:rmr-primitive.adoc[RMR Primitive Specification] - -=== RMO (Obliterative Wipe) - -[quote] -A primitive for data erasure that achieves Formal Obliteration, proving the non-existence of specified data traces. - -RMO provides: - -* Cryptographically verifiable deletion -* Proof of non-existence -* Compliance with right-to-erasure -* User autonomy with mathematical guarantees - -See: link:rmo-primitive.adoc[RMO Primitive Specification] - -== The MAA Dynamic - -The key insight of MAA is that RMR and RMO are not alternatives—they are complementary: - -[ditaa] -.... - +------------------+ - | MAA Framework | - +--------+---------+ - | - +-------------------+-------------------+ - | | - +----v----+ +----v----+ - | RMR | | RMO | - +---------+ +---------+ - | Tracks | | Removes | - | changes | Must be balanced | traces | - | fully |<--------------------------->| fully | - +---------+ +---------+ - | | - +----v----+ +----v----+ - | Audit | | Privacy | - | Trail | | Control | - +---------+ +---------+ -.... - -=== Balancing Rules - -1. **RMR-by-default**: All operations use RMR for reversibility -2. **RMO-on-request**: Users can invoke RMO to permanently erase -3. **Audit of RMO**: The _fact_ that RMO was invoked is logged (but not the erased content) -4. **No silent deletion**: RMO requires explicit user action - -== Formal Properties - -=== Property 1: Complete Reversibility (RMR) - -For any sequence of operations stem:[O_1, O_2, \ldots, O_n]: - -[stem] -++++ -O_n^{-1}(O_{n-1}^{-1}(\ldots O_1^{-1}(O_1(O_2(\ldots O_n(S)))))) = S -++++ - -The system can always return to any previous state. - -=== Property 2: Formal Obliteration (RMO) - -After stem:[\text{RMO}(d)] for data stem:[d]: - -[stem] -++++ -\nexists \text{ algorithm } A: A(\text{system\_state}) \to d -++++ - -No algorithm can recover the obliterated data from system state. - -=== Property 3: Accountability Preservation - -[stem] -++++ -\forall \text{RMO}(d): \text{log}(\text{timestamp}, \text{user}, \text{hash}(d)) -++++ - -The fact of deletion is logged, maintaining accountability without content preservation. - -== Implementation in JanusKey - -JanusKey currently implements RMR fully. RMO support is planned. - -=== Current RMR Implementation - -[source,rust] ----- -// Every operation stores complete reversal information -pub struct OperationMetadata { - pub id: String, // Unique identifier - pub op_type: OperationType, // What operation - pub timestamp: DateTime, // When - pub user: String, // Who - pub path: PathBuf, // What file - pub content_hash: Option, // Original content - pub original_metadata: Option, // Original metadata - // ... sufficient for complete reversal -} ----- - -=== Planned RMO Implementation - -[source,rust] ----- -// Future RMO primitive -pub struct ObliterationRecord { - pub timestamp: DateTime, - pub user: String, - pub content_hash: ContentHash, // Hash of obliterated content - pub obliteration_proof: ObliterationProof, -} - -// Secure deletion with overwrite -pub fn obliterate(content_hash: &ContentHash) -> Result { - // 1. Locate content in store - // 2. Overwrite with cryptographic randomness - // 3. Remove from content index - // 4. Generate non-existence proof - // 5. Log obliteration record (without content) -} ----- - -== Compliance Mapping - -|=== -| Regulation | Requirement | MAA Component - -| GDPR Art. 17 -| Right to erasure -| RMO primitive - -| GDPR Art. 5(1)(e) -| Storage limitation -| RMO with retention policies - -| GDPR Art. 30 -| Records of processing -| RMR audit trail - -| SOX Section 802 -| Record retention -| RMR full history - -| HIPAA -| Audit controls -| RMR operation logging - -| CCPA -| Right to delete -| RMO primitive -|=== - -== Security Considerations - -=== Threat Model - -|=== -| Threat | RMR Protection | RMO Protection - -| Accidental deletion -| Full reversal capability -| N/A (RMO is intentional) - -| Malicious modification -| Detect via audit trail -| N/A - -| Data breach (compromised data) -| N/A -| Obliterate compromised content - -| Compliance audit failure -| Complete audit trail -| Obliteration records -|=== - -=== Attack Surface - -The MAA framework's security relies on: - -1. **Content store integrity** (SHA256 verification) -2. **Metadata store integrity** (append-only log) -3. **RMO implementation correctness** (secure overwrite) - -== Theoretical Foundations - -MAA draws from: - -=== Bennett's Reversible Computing (1973) - -Charles Bennett proved that all computation can be made reversible without significant overhead. JanusKey applies this to file operations. - -=== Landauer's Principle (1961) - -Rolf Landauer established that erasing information has a minimum thermodynamic cost. RMO acknowledges that true erasure is an irreversible physical process. - -=== Epistemological Certainty - -Traditional logging provides _evidence_ of state changes. MAA provides _proof_: - -* RMR: Mathematical proof of reversibility -* RMO: Mathematical proof of non-existence - -== Further Reading - -* link:rmr-primitive.adoc[RMR Primitive Specification] -* link:rmo-primitive.adoc[RMO Primitive Specification] -* link:../formal-proofs/reversibility.adoc[Formal Proofs of Reversibility] -* link:mpr.adoc[Maximal Principle Reduction] diff --git a/docs/wiki/theory/maa-framework.md b/docs/wiki/theory/maa-framework.md new file mode 100644 index 0000000..0e20a9b --- /dev/null +++ b/docs/wiki/theory/maa-framework.md @@ -0,0 +1,253 @@ + + + +# Mutually Assured Accountability (MAA) Framework + +## Abstract + +The MAA Framework is an ethical and engineering paradigm that structurally balances two opposing requirements in data management systems: + +1. **Accountability/Auditability**: Systems must track changes for compliance, debugging, and recovery + +2. **User Autonomy/Privacy**: Users must have verifiable control over their data, including deletion + +These requirements are often in tension. MAA resolves this through formal primitives that provide mathematical guarantees for both. + +## The Problem Statement + +### The Accountability Requirement + +Modern systems require auditability for: + +- **Regulatory compliance** (SOX, HIPAA, financial regulations) + +- **Security forensics** (incident investigation) + +- **System reliability** (debugging, recovery) + +- **Business continuity** (disaster recovery) + +This pushes toward: *never delete anything, log everything, keep all history*. + +### The Autonomy Requirement + +Users and regulations require data control: + +- **GDPR Article 17** (Right to Erasure) + +- **CCPA** (California Consumer Privacy Act) + +- **User trust** (promise of deletion must be kept) + +- **Security** (compromised data should be destroyable) + +This pushes toward: *complete, verifiable deletion capability*. + +### The Tension + + Accountability <---------> Autonomy + "Keep everything" "Delete completely" + + Traditional solutions: + +------------------+ or +------------------+ + | Keep all data | | Allow deletion | + | No real deletion | | No audit trail | + +------------------+ +------------------+ + + Result: Always compromise one goal + +## The MAA Solution + +MAA introduces two formally verified primitives that satisfy both requirements: + +### RMR (Reversible Transaction) + +> A primitive for state transitions that guarantees instant, mathematically certain rollback to a prior safe state via Algorithmic Reversibility. + +RMR provides: + +- Complete state capture before changes + +- Guaranteed reversal capability + +- Full audit trail of all operations + +- Accountability without data loss risk + +See: [RMR Primitive Specification](rmr-primitive.md) + +### RMO (Obliterative Wipe) + +> A primitive for data erasure that achieves Formal Obliteration, proving the non-existence of specified data traces. + +RMO provides: + +- Cryptographically verifiable deletion + +- Proof of non-existence + +- Compliance with right-to-erasure + +- User autonomy with mathematical guarantees + +See: [RMO Primitive Specification](rmo-primitive.md) + +## The MAA Dynamic + +The key insight of MAA is that RMR and RMO are not alternatives—they are complementary: + + +------------------+ + | MAA Framework | + +--------+---------+ + | + +-------------------+-------------------+ + | | + +----v----+ +----v----+ + | RMR | | RMO | + +---------+ +---------+ + | Tracks | | Removes | + | changes | Must be balanced | traces | + | fully |<--------------------------->| fully | + +---------+ +---------+ + | | + +----v----+ +----v----+ + | Audit | | Privacy | + | Trail | | Control | + +---------+ +---------+ + +### Balancing Rules + +1. **RMR-by-default**: All operations use RMR for reversibility + +2. **RMO-on-request**: Users can invoke RMO to permanently erase + +3. **Audit of RMO**: The *fact* that RMO was invoked is logged (but not the erased content) + +4. **No silent deletion**: RMO requires explicit user action + +## Formal Properties + +### Property 1: Complete Reversibility (RMR) + +For any sequence of operations $O_1, O_2, \ldots, O_n$: + +$$O_n^{-1}(O_{n-1}^{-1}(\ldots O_1^{-1}(O_1(O_2(\ldots O_n(S)))))) = S$$ + +The system can always return to any previous state. + +### Property 2: Formal Obliteration (RMO) + +After $\text{RMO}(d)$ for data $d$: + +$$\nexists \text{ algorithm } A: A(\text{system\_state}) \to d$$ + +No algorithm can recover the obliterated data from system state. + +### Property 3: Accountability Preservation + +$$\forall \text{RMO}(d): \text{log}(\text{timestamp}, \text{user}, \text{hash}(d))$$ + +The fact of deletion is logged, maintaining accountability without content preservation. + +## Implementation in JanusKey + +JanusKey currently implements RMR fully. RMO support is planned. + +### Current RMR Implementation + +``` rust +// Every operation stores complete reversal information +pub struct OperationMetadata { + pub id: String, // Unique identifier + pub op_type: OperationType, // What operation + pub timestamp: DateTime, // When + pub user: String, // Who + pub path: PathBuf, // What file + pub content_hash: Option, // Original content + pub original_metadata: Option, // Original metadata + // ... sufficient for complete reversal +} +``` + +### Planned RMO Implementation + +``` rust +// Future RMO primitive +pub struct ObliterationRecord { + pub timestamp: DateTime, + pub user: String, + pub content_hash: ContentHash, // Hash of obliterated content + pub obliteration_proof: ObliterationProof, +} + +// Secure deletion with overwrite +pub fn obliterate(content_hash: &ContentHash) -> Result { + // 1. Locate content in store + // 2. Overwrite with cryptographic randomness + // 3. Remove from content index + // 4. Generate non-existence proof + // 5. Log obliteration record (without content) +} +``` + +## Compliance Mapping + +| Regulation | Requirement | MAA Component | +|-------------------|-----------------------|-----------------------------| +| GDPR Art. 17 | Right to erasure | RMO primitive | +| GDPR Art. 5(1)(e) | Storage limitation | RMO with retention policies | +| GDPR Art. 30 | Records of processing | RMR audit trail | +| SOX Section 802 | Record retention | RMR full history | +| HIPAA | Audit controls | RMR operation logging | +| CCPA | Right to delete | RMO primitive | + +## Security Considerations + +### Threat Model + +| Threat | RMR Protection | RMO Protection | +|--------------------------------|--------------------------|--------------------------------| +| Accidental deletion | Full reversal capability | N/A (RMO is intentional) | +| Malicious modification | Detect via audit trail | N/A | +| Data breach (compromised data) | N/A | Obliterate compromised content | +| Compliance audit failure | Complete audit trail | Obliteration records | + +### Attack Surface + +The MAA framework’s security relies on: + +1. **Content store integrity** (SHA256 verification) + +2. **Metadata store integrity** (append-only log) + +3. **RMO implementation correctness** (secure overwrite) + +## Theoretical Foundations + +MAA draws from: + +### Bennett’s Reversible Computing (1973) + +Charles Bennett proved that all computation can be made reversible without significant overhead. JanusKey applies this to file operations. + +### Landauer’s Principle (1961) + +Rolf Landauer established that erasing information has a minimum thermodynamic cost. RMO acknowledges that true erasure is an irreversible physical process. + +### Epistemological Certainty + +Traditional logging provides *evidence* of state changes. MAA provides *proof*: + +- RMR: Mathematical proof of reversibility + +- RMO: Mathematical proof of non-existence + +## Further Reading + +- [RMR Primitive Specification](rmr-primitive.md) + +- [RMO Primitive Specification](rmo-primitive.md) + +- [Formal Proofs of Reversibility](../formal-proofs/reversibility.md) + +- [Maximal Principle Reduction](mpr.md) diff --git a/docs/wiki/theory/mpr.adoc b/docs/wiki/theory/mpr.adoc deleted file mode 100644 index 5c22452..0000000 --- a/docs/wiki/theory/mpr.adoc +++ /dev/null @@ -1,275 +0,0 @@ -// SPDX-License-Identifier: CC-BY-SA-4.0 -// Copyright (c) Jonathan D.A. Jewell -= Maximal Principle Reduction (MPR) -:toc: left -:toclevels: 3 -:icons: font -:source-highlighter: rouge -:stem: latexmath - -== Definition - -**Maximal Principle Reduction (MPR)** is a design philosophy and engineering methodology that achieves security and correctness guarantees through structural constraints rather than runtime verification. - -[quote, Core MPR Axiom] -If a vulnerability cannot exist in the design space, it cannot exist in the implementation. Reduce the design space to exclude vulnerabilities entirely. - -== Principles - -=== Principle 1: Construction Over Verification - -[cols="1,1"] -|=== -| Verification Approach | Construction Approach - -| Write code, then test for bugs -| Design types that make bugs unrepresentable - -| Add validation at boundaries -| Make invalid states impossible - -| Log errors for detection -| Prevent error conditions structurally -|=== - -.Example: Null Safety -[source,rust] ----- -// Verification approach: check at runtime -fn process_file(path: String) -> Result<(), Error> { - if path.is_empty() { - return Err(Error::InvalidPath); - } - // ... process -} - -// MPR approach: make empty paths unrepresentable -struct NonEmptyPath(String); - -impl NonEmptyPath { - fn new(s: String) -> Option { - if s.is_empty() { None } else { Some(NonEmptyPath(s)) } - } -} - -fn process_file(path: NonEmptyPath) { - // No runtime check needed - empty paths cannot exist -} ----- - -=== Principle 2: Minimal Trusted Computing Base - -The **Trusted Computing Base (TCB)** is the set of components that must be correct for the system to be secure. MPR mandates: - -1. Minimize the TCB to the smallest possible set -2. Formally verify the TCB where possible -3. Use simple, auditable code for TCB components - -.JanusKey TCB ----- -+------------------------------------------+ -| Application Code | <- Not in TCB -+------------------------------------------+ -| Operation Executor | <- Not in TCB -+------------------------------------------+ -| Content Store | Metadata Store | <- Minimal TCB -+------------------------------------------+ -| SHA256 | File System | <- External TCB -+------------------------------------------+ ----- - -=== Principle 3: Structural Invariants - -Invariants should be enforced by the type system and data structures, not runtime checks. - -.JanusKey Structural Invariants -|=== -| Invariant | Enforcement Mechanism - -| Content integrity -| SHA256 content-addressed storage (verified on retrieval) - -| Operation completeness -| Metadata stored atomically with operation - -| Transaction atomicity -| All-or-nothing commit/rollback by design - -| Reversal possibility -| Inverse metadata captured before execution -|=== - -=== Principle 4: Fail-Safe Defaults - -When in doubt, the system should fail safely: - -* Operations fail if metadata cannot be stored -* Retrieval fails if content hash doesn't match -* Transactions roll back if any operation fails - -== Application to File Operations - -=== The Data Loss Problem - -Traditional file operations are inherently destructive: - -[source,bash] ----- -rm important_file.txt # Data gone forever -mv file.txt /dev/null # Data gone forever ----- - -Recovery requires external systems (backups, snapshots) that: - -* May not exist -* May be out of date -* May be corrupted -* Require manual intervention - -=== MPR Solution: Reversibility by Construction - -JanusKey applies MPR by making irreversible operations structurally impossible: - -[source,rust] ----- -// Before any destructive operation: -// 1. Capture complete content -// 2. Capture complete metadata -// 3. Store in content-addressed storage -// 4. Log operation with inverse information -// 5. Only then execute - -pub fn execute_delete(&mut self, path: &Path) -> Result { - // Capture everything needed for reversal FIRST - let content = fs::read(path)?; // Full content - let file_metadata = FileMetadata::from_path(path)?; // All metadata - let content_hash = self.content_store.store(&content)?; // Store safely - - // Create metadata with all information for reversal - let metadata = OperationMetadata::new(OperationType::Delete, path) - .with_content_hash(content_hash) - .with_original_metadata(file_metadata); - - // NOW safe to delete - reversal is guaranteed possible - fs::remove_file(path)?; - - self.metadata_store.append(metadata)?; - Ok(metadata) -} ----- - -== Comparison with Other Approaches - -=== Version Control (Git) - -[cols="1,1,1"] -|=== -| Aspect | Git | JanusKey - -| Scope -| Tracked files only -| Any file operation - -| Granularity -| Commit-level -| Operation-level - -| Explicit action required -| Yes (git add, commit) -| No (automatic) - -| Working directory changes -| Lost until committed -| Always reversible -|=== - -=== File System Snapshots (ZFS, Btrfs) - -[cols="1,1,1"] -|=== -| Aspect | Snapshots | JanusKey - -| Recovery granularity -| Snapshot-level -| Operation-level - -| Space efficiency -| Copy-on-write -| Content-addressed deduplication - -| Cross-filesystem -| No -| Yes - -| Operation awareness -| None -| Full semantic awareness -|=== - -=== Traditional Backups - -[cols="1,1,1"] -|=== -| Aspect | Backups | JanusKey - -| Recovery time -| Minutes to hours -| Milliseconds - -| Recovery point -| Last backup time -| Any operation - -| Storage overhead -| Full copies -| Deduplicated content - -| Requires external system -| Yes -| No (self-contained) -|=== - -== Formal Statement - -Let stem:[D] be the design space of all possible system states and behaviors. - -Let stem:[V \subset D] be the set of vulnerable or incorrect states. - -**Traditional approach**: Implement stem:[D], then try to detect/prevent stem:[V] at runtime. - -**MPR approach**: Design a restricted space stem:[D' \subset D] such that stem:[D' \cap V = \emptyset]. - -For JanusKey: -[stem] -++++ -D' = \{ s \in D : \forall o \in \text{ops}(s), \exists o^{-1} \in \text{metadata}(s) \} -++++ - -This ensures that any reachable state has complete reversal information by construction. - -== Limitations and Trade-offs - -=== Storage Overhead - -Content must be stored before deletion. This trades: -* Storage space (more) for reversibility (guaranteed) -* Mitigated by: compression, deduplication, garbage collection - -=== Performance Overhead - -Additional I/O for metadata capture. This trades: -* Latency (higher) for safety (guaranteed) -* Mitigated by: async operations, batching, transactions - -=== Complexity Shift - -Complexity moves from recovery procedures to operation design: -* Simpler recovery (just undo) -* More complex operation implementation -* Net reduction in total system complexity - -== Further Reading - -* link:maa-framework.adoc[MAA Framework - Balancing Auditability and Autonomy] -* link:formal-model.adoc[Formal Model - Mathematical Definitions] -* link:../formal-proofs/index.adoc[Formal Proofs of Reversibility] diff --git a/docs/wiki/theory/mpr.md b/docs/wiki/theory/mpr.md new file mode 100644 index 0000000..b2adbc7 --- /dev/null +++ b/docs/wiki/theory/mpr.md @@ -0,0 +1,211 @@ + + + +# Maximal Principle Reduction (MPR) + +## Definition + +**Maximal Principle Reduction (MPR)** is a design philosophy and engineering methodology that achieves security and correctness guarantees through structural constraints rather than runtime verification. + +> If a vulnerability cannot exist in the design space, it cannot exist in the implementation. Reduce the design space to exclude vulnerabilities entirely. +> +> — Core MPR Axiom + +## Principles + +### Principle 1: Construction Over Verification + +| Verification Approach | Construction Approach | +|--------------------------------|---------------------------------------------| +| Write code, then test for bugs | Design types that make bugs unrepresentable | +| Add validation at boundaries | Make invalid states impossible | +| Log errors for detection | Prevent error conditions structurally | + +**Example: Null Safety** + +``` rust +// Verification approach: check at runtime +fn process_file(path: String) -> Result<(), Error> { + if path.is_empty() { + return Err(Error::InvalidPath); + } + // ... process +} + +// MPR approach: make empty paths unrepresentable +struct NonEmptyPath(String); + +impl NonEmptyPath { + fn new(s: String) -> Option { + if s.is_empty() { None } else { Some(NonEmptyPath(s)) } + } +} + +fn process_file(path: NonEmptyPath) { + // No runtime check needed - empty paths cannot exist +} +``` + +### Principle 2: Minimal Trusted Computing Base + +The **Trusted Computing Base (TCB)** is the set of components that must be correct for the system to be secure. MPR mandates: + +1. Minimize the TCB to the smallest possible set + +2. Formally verify the TCB where possible + +3. Use simple, auditable code for TCB components + +**JanusKey TCB** + + +------------------------------------------+ + | Application Code | <- Not in TCB + +------------------------------------------+ + | Operation Executor | <- Not in TCB + +------------------------------------------+ + | Content Store | Metadata Store | <- Minimal TCB + +------------------------------------------+ + | SHA256 | File System | <- External TCB + +------------------------------------------+ + +### Principle 3: Structural Invariants + +Invariants should be enforced by the type system and data structures, not runtime checks. + +| Invariant | Enforcement Mechanism | +|------------------------|----------------------------------------------------------| +| Content integrity | SHA256 content-addressed storage (verified on retrieval) | +| Operation completeness | Metadata stored atomically with operation | +| Transaction atomicity | All-or-nothing commit/rollback by design | +| Reversal possibility | Inverse metadata captured before execution | + +JanusKey Structural Invariants + +### Principle 4: Fail-Safe Defaults + +When in doubt, the system should fail safely: + +- Operations fail if metadata cannot be stored + +- Retrieval fails if content hash doesn’t match + +- Transactions roll back if any operation fails + +## Application to File Operations + +### The Data Loss Problem + +Traditional file operations are inherently destructive: + +``` bash +rm important_file.txt # Data gone forever +mv file.txt /dev/null # Data gone forever +``` + +Recovery requires external systems (backups, snapshots) that: + +- May not exist + +- May be out of date + +- May be corrupted + +- Require manual intervention + +### MPR Solution: Reversibility by Construction + +JanusKey applies MPR by making irreversible operations structurally impossible: + +``` rust +// Before any destructive operation: +// 1. Capture complete content +// 2. Capture complete metadata +// 3. Store in content-addressed storage +// 4. Log operation with inverse information +// 5. Only then execute + +pub fn execute_delete(&mut self, path: &Path) -> Result { + // Capture everything needed for reversal FIRST + let content = fs::read(path)?; // Full content + let file_metadata = FileMetadata::from_path(path)?; // All metadata + let content_hash = self.content_store.store(&content)?; // Store safely + + // Create metadata with all information for reversal + let metadata = OperationMetadata::new(OperationType::Delete, path) + .with_content_hash(content_hash) + .with_original_metadata(file_metadata); + + // NOW safe to delete - reversal is guaranteed possible + fs::remove_file(path)?; + + self.metadata_store.append(metadata)?; + Ok(metadata) +} +``` + +## Comparison with Other Approaches + +### Version Control (Git) + +| Aspect | Git | JanusKey | +|---------------------------|-----------------------|--------------------| +| Scope | Tracked files only | Any file operation | +| Granularity | Commit-level | Operation-level | +| Explicit action required | Yes (git add, commit) | No (automatic) | +| Working directory changes | Lost until committed | Always reversible | + +### File System Snapshots (ZFS, Btrfs) + +| Aspect | Snapshots | JanusKey | +|----------------------|----------------|---------------------------------| +| Recovery granularity | Snapshot-level | Operation-level | +| Space efficiency | Copy-on-write | Content-addressed deduplication | +| Cross-filesystem | No | Yes | +| Operation awareness | None | Full semantic awareness | + +### Traditional Backups + +| Aspect | Backups | JanusKey | +|--------------------------|------------------|----------------------| +| Recovery time | Minutes to hours | Milliseconds | +| Recovery point | Last backup time | Any operation | +| Storage overhead | Full copies | Deduplicated content | +| Requires external system | Yes | No (self-contained) | + +## Formal Statement + +Let $D$ be the design space of all possible system states and behaviors. + +Let $V \subset D$ be the set of vulnerable or incorrect states. + +**Traditional approach**: Implement $D$, then try to detect/prevent $V$ at runtime. + +**MPR approach**: Design a restricted space $D' \subset D$ such that $D' \cap V = \emptyset$. + +For JanusKey: + +$$D' = \{ s \in D : \forall o \in \text{ops}(s), \exists o^{-1} \in \text{metadata}(s) \}$$ + +This ensures that any reachable state has complete reversal information by construction. + +## Limitations and Trade-offs + +### Storage Overhead + +Content must be stored before deletion. This trades: \* Storage space (more) for reversibility (guaranteed) \* Mitigated by: compression, deduplication, garbage collection + +### Performance Overhead + +Additional I/O for metadata capture. This trades: \* Latency (higher) for safety (guaranteed) \* Mitigated by: async operations, batching, transactions + +### Complexity Shift + +Complexity moves from recovery procedures to operation design: \* Simpler recovery (just undo) \* More complex operation implementation \* Net reduction in total system complexity + +## Further Reading + +- [MAA Framework - Balancing Auditability and Autonomy](maa-framework.md) + +- [Formal Model - Mathematical Definitions](formal-model.md) + +- [Formal Proofs of Reversibility](../formal-proofs/index.md) diff --git a/docs/wiki/theory/rmo-primitive.adoc b/docs/wiki/theory/rmo-primitive.adoc deleted file mode 100644 index 715a604..0000000 --- a/docs/wiki/theory/rmo-primitive.adoc +++ /dev/null @@ -1,335 +0,0 @@ -// SPDX-License-Identifier: CC-BY-SA-4.0 -// Copyright (c) Jonathan D.A. Jewell -= RMO: Obliterative Wipe Primitive -:toc: left -:toclevels: 3 -:icons: font -:source-highlighter: rouge -:stem: latexmath - -== Definition - -**RMO (Obliterative Wipe)** is a data primitive that guarantees, via formal proof, the non-existence (unrecoverability) of specific data after invocation. This implements GDPR Article 17 "Right to Erasure" with mathematical certainty. - -[quote] -For data stem:[d] after stem:[\text{RMO}(d)], there exists no algorithm stem:[A] such that stem:[A(\text{system\_state}) = d]. - -== Core Properties - -=== Property 1: Irrecoverability - -After obliteration, the data cannot be recovered through any algorithmic means: - -[stem] -++++ -\nexists A: A(\Sigma', M') \to d -++++ - -where stem:[\Sigma'] and stem:[M'] are the post-obliteration content and metadata stores. - -=== Property 2: Verifiable Deletion - -Obliteration generates a cryptographic proof that can be independently verified: - -[source,rust] ----- -pub struct ObliterationProof { - pub id: String, - pub content_hash: ContentHash, - pub timestamp: DateTime, - pub nonce: String, - pub commitment: String, // H(content_hash || nonce || timestamp) - pub overwrite_passes: usize, -} ----- - -=== Property 3: Audit Trail Without Content - -The fact of obliteration is logged for compliance, but the content itself is never stored in the audit log: - -[source,rust] ----- -pub struct ObliterationRecord { - pub timestamp: DateTime, - pub user: String, - pub content_hash: ContentHash, // Hash only, not content - pub reason: Option, - pub legal_basis: Option, - pub proof: ObliterationProof, -} ----- - -== Secure Overwrite Protocol - -RMO implements a multi-pass secure overwrite based on DoD 5220.22-M standards: - -=== Phase 1: Pattern Overwrite - -[source,rust] ----- -const OVERWRITE_PASSES: usize = 3; -const PATTERNS: [u8; 3] = [0x00, 0xFF, 0x00]; - -// Pass 1: Write all zeros -// Pass 2: Write all ones -// Pass 3: Random data ----- - -=== Phase 2: File Removal - -After secure overwrite, the file is deleted from the filesystem. - -=== Phase 3: Proof Generation - -A cryptographic commitment is generated: - -[stem] -++++ -\text{commitment} = H(\text{content\_hash} \| \text{nonce} \| \text{timestamp}) -++++ - -This allows verification that: - -1. The specific content (identified by hash) was obliterated -2. Obliteration occurred at a specific time -3. The proof has not been tampered with - -== CLI Commands - -=== Obliterate Content - -[source,bash] ----- -# Obliterate by content hash -jk obliterate --hash sha256:abc123... --reason "User request" --legal-basis "GDPR Article 17" - -# Obliterate content from a specific operation -jk obliterate --operation --reason "Data breach cleanup" ----- - -=== View Obliteration History - -[source,bash] ----- -jk obliteration-history --limit 20 ----- - -Output: ----- -Obliteration History (RMO Audit Trail) -════════════════════════════════════════════════════════════════════════════════ - -2025-12-17 10:30:00 UTC | OBLITERATED | sha256:abc123... - Proof: 12345678 | User: jonathan - Reason: User request - Legal basis: GDPR Article 17 - -════════════════════════════════════════════════════════════════════════════════ -Total obliterations: 1 ----- - -=== Verify Obliteration Proof - -[source,bash] ----- -jk verify-obliteration ----- - -Output: ----- -Obliteration Proof Verification -────────────────────────────────────────────────── - -Proof ID: abc123-def456... -Content Hash: sha256:abc123... -Timestamp: 2025-12-17 10:30:00 UTC -User: jonathan -Overwrite Passes: 3 - -Commitment: 9f86d081884c7d659a2feaa0c55ad015... -Nonce: a3f2b8c9d0e1f2a3... - -✓ Cryptographic commitment VERIFIED - The proof cryptographically confirms that: - • Content with hash sha256:abc123... was obliterated - • Obliteration occurred at 2025-12-17 10:30:00 - • 3 overwrite passes were performed - -✓ Content confirmed absent from store ----- - -== Relationship with RMR - -RMR and RMO are complementary primitives in the MAA Framework: - -[ditaa] -.... - +---------------+ +---------------+ - | RMR | | RMO | - | Reversibility | | Obliteration | - +-------+-------+ +-------+-------+ - | | - | Normal operation | - +------------------------------------> - | | - | Can undo via RMR | - <------------------------------------+ - | | - | GDPR erasure request | - +------------------------------------> - | | - | After RMO: NO undo | - | Content unrecoverable | - | | - +-------+-------+ +-------+-------+ - | Accountability| | Autonomy | - +---------------+ +---------------+ -.... - -=== Key Differences - -|=== -| Aspect | RMR | RMO - -| Purpose -| Track and reverse changes -| Permanently erase data - -| Reversibility -| Always reversible -| Never reversible - -| Content storage -| Preserves original content -| Destroys original content - -| Audit -| Full operation details -| Fact of deletion only - -| Legal basis -| Accountability -| Right to Erasure -|=== - -== GDPR Compliance - -RMO directly supports GDPR Article 17 "Right to Erasure": - -=== Article 17 Requirements - -[quote, GDPR Article 17] -The data subject shall have the right to obtain from the controller the erasure of personal data concerning him or her without undue delay. - -=== How RMO Complies - -|=== -| GDPR Requirement | RMO Implementation - -| Erasure without undue delay -| Immediate secure overwrite - -| Verification of erasure -| Cryptographic obliteration proof - -| Record of erasure -| Audit log with timestamp, user, legal basis - -| No retention of erased data -| Proof contains only hash, not content -|=== - -== Formal Verification - -=== Theorem: Formal Obliteration - -After stem:[\text{RMO}(h)] for content hash stem:[h]: - -[stem] -++++ -\Sigma(h) = \bot \land \text{proof\_valid}(h) -++++ - -The content is removed from the store and a valid proof exists. - -=== Proof - -1. **Secure Overwrite**: Content at path derived from stem:[h] is overwritten with patterns -2. **File Removal**: Path is deleted from filesystem -3. **Proof Generation**: Commitment stem:[H(h \| n \| t)] is computed -4. **Store Update**: stem:[\Sigma(h) := \bot] - -By the irreversibility of secure overwrite and the collision resistance of SHA256, the content is unrecoverable. ∎ - -== Implementation - -[source,rust] ----- -impl ObliterationManager { - pub fn obliterate( - &mut self, - content_store: &ContentStore, - content_hash: &ContentHash, - reason: Option, - legal_basis: Option, - ) -> Result { - // Get content path - let content_path = content_store.content_path(content_hash); - - // Perform secure overwrite - let passes = secure_overwrite(&content_path)?; - - // Remove file - fs::remove_file(&content_path)?; - - // Generate proof - let proof = ObliterationProof::generate(content_hash, passes); - - // Create audit record - let record = ObliterationRecord { - id: Uuid::new_v4().to_string(), - timestamp: Utc::now(), - user: whoami::username(), - content_hash: content_hash.clone(), - reason, - legal_basis, - proof, - cleaned_operation_ids: Vec::new(), - }; - - // Log for audit - self.log.records.push(record.clone()); - self.save()?; - - Ok(record) - } -} ----- - -== Security Considerations - -=== Secure Overwrite Limitations - -* Does not guarantee erasure from SSDs with wear leveling -* May not erase data from filesystem journal -* Cloud storage may retain backups - -For maximum security: - -1. Use encrypted storage where possible -2. Consider physical destruction for highly sensitive data -3. Verify with specialized forensic tools - -=== Proof Tamper Resistance - -The cryptographic commitment prevents: - -* Backdating obliteration claims -* Claiming obliteration of different content -* Modifying the proof after generation - -== Further Reading - -* link:rmr-primitive.adoc[RMR: Reversible Transaction Primitive] -* link:maa-framework.adoc[MAA Framework Overview] -* link:../guides/cli.adoc[CLI Commands Guide] diff --git a/docs/wiki/theory/rmo-primitive.md b/docs/wiki/theory/rmo-primitive.md new file mode 100644 index 0000000..18a79c3 --- /dev/null +++ b/docs/wiki/theory/rmo-primitive.md @@ -0,0 +1,298 @@ + + + +# RMO: Obliterative Wipe Primitive + +## Definition + +**RMO (Obliterative Wipe)** is a data primitive that guarantees, via formal proof, the non-existence (unrecoverability) of specific data after invocation. This implements GDPR Article 17 "Right to Erasure" with mathematical certainty. + +> For data $d$ after $\text{RMO}(d)$, there exists no algorithm $A$ such that $A(\text{system\_state}) = d$. + +## Core Properties + +### Property 1: Irrecoverability + +After obliteration, the data cannot be recovered through any algorithmic means: + +$$\nexists A: A(\Sigma', M') \to d$$ + +where $\Sigma'$ and $M'$ are the post-obliteration content and metadata stores. + +### Property 2: Verifiable Deletion + +Obliteration generates a cryptographic proof that can be independently verified: + +``` rust +pub struct ObliterationProof { + pub id: String, + pub content_hash: ContentHash, + pub timestamp: DateTime, + pub nonce: String, + pub commitment: String, // H(content_hash || nonce || timestamp) + pub overwrite_passes: usize, +} +``` + +### Property 3: Audit Trail Without Content + +The fact of obliteration is logged for compliance, but the content itself is never stored in the audit log: + +``` rust +pub struct ObliterationRecord { + pub timestamp: DateTime, + pub user: String, + pub content_hash: ContentHash, // Hash only, not content + pub reason: Option, + pub legal_basis: Option, + pub proof: ObliterationProof, +} +``` + +## Secure Overwrite Protocol + +RMO implements a multi-pass secure overwrite based on DoD 5220.22-M standards: + +### Phase 1: Pattern Overwrite + +``` rust +const OVERWRITE_PASSES: usize = 3; +const PATTERNS: [u8; 3] = [0x00, 0xFF, 0x00]; + +// Pass 1: Write all zeros +// Pass 2: Write all ones +// Pass 3: Random data +``` + +### Phase 2: File Removal + +After secure overwrite, the file is deleted from the filesystem. + +### Phase 3: Proof Generation + +A cryptographic commitment is generated: + +$$\text{commitment} = H(\text{content\_hash} \| \text{nonce} \| \text{timestamp})$$ + +This allows verification that: + +1. The specific content (identified by hash) was obliterated + +2. Obliteration occurred at a specific time + +3. The proof has not been tampered with + +## CLI Commands + +### Obliterate Content + +``` bash +# Obliterate by content hash +jk obliterate --hash sha256:abc123... --reason "User request" --legal-basis "GDPR Article 17" + +# Obliterate content from a specific operation +jk obliterate --operation --reason "Data breach cleanup" +``` + +### View Obliteration History + +``` bash +jk obliteration-history --limit 20 +``` + +Output: + + Obliteration History (RMO Audit Trail) + ════════════════════════════════════════════════════════════════════════════════ + + 2025-12-17 10:30:00 UTC | OBLITERATED | sha256:abc123... + Proof: 12345678 | User: jonathan + Reason: User request + Legal basis: GDPR Article 17 + + ════════════════════════════════════════════════════════════════════════════════ + Total obliterations: 1 + +### Verify Obliteration Proof + +``` bash +jk verify-obliteration +``` + +Output: + + Obliteration Proof Verification + ────────────────────────────────────────────────── + + Proof ID: abc123-def456... + Content Hash: sha256:abc123... + Timestamp: 2025-12-17 10:30:00 UTC + User: jonathan + Overwrite Passes: 3 + + Commitment: 9f86d081884c7d659a2feaa0c55ad015... + Nonce: a3f2b8c9d0e1f2a3... + + ✓ Cryptographic commitment VERIFIED + The proof cryptographically confirms that: + • Content with hash sha256:abc123... was obliterated + • Obliteration occurred at 2025-12-17 10:30:00 + • 3 overwrite passes were performed + + ✓ Content confirmed absent from store + +## Relationship with RMR + +RMR and RMO are complementary primitives in the MAA Framework: + + +---------------+ +---------------+ + | RMR | | RMO | + | Reversibility | | Obliteration | + +-------+-------+ +-------+-------+ + | | + | Normal operation | + +------------------------------------> + | | + | Can undo via RMR | + <------------------------------------+ + | | + | GDPR erasure request | + +------------------------------------> + | | + | After RMO: NO undo | + | Content unrecoverable | + | | + +-------+-------+ +-------+-------+ + | Accountability| | Autonomy | + +---------------+ +---------------+ + +### Key Differences + +| Aspect | RMR | RMO | +|-----------------|----------------------------|---------------------------| +| Purpose | Track and reverse changes | Permanently erase data | +| Reversibility | Always reversible | Never reversible | +| Content storage | Preserves original content | Destroys original content | +| Audit | Full operation details | Fact of deletion only | +| Legal basis | Accountability | Right to Erasure | + +## GDPR Compliance + +RMO directly supports GDPR Article 17 "Right to Erasure": + +### Article 17 Requirements + +> The data subject shall have the right to obtain from the controller the erasure of personal data concerning him or her without undue delay. +> +> — GDPR Article 17 + +### How RMO Complies + +| GDPR Requirement | RMO Implementation | +|-----------------------------|---------------------------------------------| +| Erasure without undue delay | Immediate secure overwrite | +| Verification of erasure | Cryptographic obliteration proof | +| Record of erasure | Audit log with timestamp, user, legal basis | +| No retention of erased data | Proof contains only hash, not content | + +## Formal Verification + +### Theorem: Formal Obliteration + +After $\text{RMO}(h)$ for content hash $h$: + +$$\Sigma(h) = \bot \land \text{proof\_valid}(h)$$ + +The content is removed from the store and a valid proof exists. + +### Proof + +1. **Secure Overwrite**: Content at path derived from $h$ is overwritten with patterns + +2. **File Removal**: Path is deleted from filesystem + +3. **Proof Generation**: Commitment $H(h \| n \| t)$ is computed + +4. **Store Update**: $\Sigma(h) := \bot$ + +By the irreversibility of secure overwrite and the collision resistance of SHA256, the content is unrecoverable. ∎ + +## Implementation + +``` rust +impl ObliterationManager { + pub fn obliterate( + &mut self, + content_store: &ContentStore, + content_hash: &ContentHash, + reason: Option, + legal_basis: Option, + ) -> Result { + // Get content path + let content_path = content_store.content_path(content_hash); + + // Perform secure overwrite + let passes = secure_overwrite(&content_path)?; + + // Remove file + fs::remove_file(&content_path)?; + + // Generate proof + let proof = ObliterationProof::generate(content_hash, passes); + + // Create audit record + let record = ObliterationRecord { + id: Uuid::new_v4().to_string(), + timestamp: Utc::now(), + user: whoami::username(), + content_hash: content_hash.clone(), + reason, + legal_basis, + proof, + cleaned_operation_ids: Vec::new(), + }; + + // Log for audit + self.log.records.push(record.clone()); + self.save()?; + + Ok(record) + } +} +``` + +## Security Considerations + +### Secure Overwrite Limitations + +- Does not guarantee erasure from SSDs with wear leveling + +- May not erase data from filesystem journal + +- Cloud storage may retain backups + +For maximum security: + +1. Use encrypted storage where possible + +2. Consider physical destruction for highly sensitive data + +3. Verify with specialized forensic tools + +### Proof Tamper Resistance + +The cryptographic commitment prevents: + +- Backdating obliteration claims + +- Claiming obliteration of different content + +- Modifying the proof after generation + +## Further Reading + +- [RMR: Reversible Transaction Primitive](rmr-primitive.md) + +- [MAA Framework Overview](maa-framework.md) + +- [CLI Commands Guide](../guides/cli.md) diff --git a/docs/wiki/theory/rmr-primitive.adoc b/docs/wiki/theory/rmr-primitive.adoc deleted file mode 100644 index 9793458..0000000 --- a/docs/wiki/theory/rmr-primitive.adoc +++ /dev/null @@ -1,387 +0,0 @@ -// SPDX-License-Identifier: CC-BY-SA-4.0 -// Copyright (c) Jonathan D.A. Jewell -= RMR: Reversible Transaction Primitive -:toc: left -:toclevels: 3 -:icons: font -:source-highlighter: rouge -:stem: latexmath - -== Definition - -**RMR (Reversible Transaction)** is a data primitive that guarantees the existence of a mathematically sound inverse function for every state change. - -[quote] -For every operation stem:[F] applied to state stem:[S], there exists an inverse stem:[F^{-1}] such that stem:[F^{-1}(F(S)) = S]. - -== Core Properties - -=== Property 1: Deterministic Reversal - -Given an operation and its metadata, the reversal is uniquely determined: - -[stem] -++++ -\text{undo}: (\text{Operation}, \text{Metadata}) \to \text{Operation}^{-1} -++++ - -There is no ambiguity in how to reverse an operation. - -=== Property 2: State Preservation - -The reversal restores _complete_ original state, including: - -* File content (byte-for-byte identical) -* File metadata (permissions, ownership, timestamps) -* Directory structure -* Symbolic link targets - -=== Property 3: Sequential Composition - -For a sequence of operations, reversals compose correctly: - -[stem] -++++ -(O_2 \circ O_1)^{-1} = O_1^{-1} \circ O_2^{-1} -++++ - -Operations are undone in reverse order. - -=== Property 4: Transaction Atomicity - -Operations grouped in a transaction satisfy: - -* All-or-nothing execution -* All-or-nothing reversal -* Intermediate states are not externally visible - -== RMR Protocol - -=== Phase 1: Pre-Capture - -Before any modification, capture everything needed for reversal: - -[source,rust] ----- -// For a DELETE operation -fn pre_capture_delete(path: &Path) -> PreCaptureData { - PreCaptureData { - content: fs::read(path), // Full file content - metadata: FileMetadata::from_path(path), // All metadata - content_hash: sha256(content), // For verification - } -} ----- - -=== Phase 2: Secure Storage - -Store captured data in content-addressed storage: - -[source,rust] ----- -fn secure_store(data: PreCaptureData) -> StorageReceipt { - let hash = content_store.store(data.content)?; - // Content is now safely stored with deduplication - StorageReceipt { hash, verified: true } -} ----- - -=== Phase 3: Metadata Recording - -Create complete operation metadata: - -[source,rust] ----- -fn record_metadata(op: Operation, receipt: StorageReceipt) -> OperationMetadata { - OperationMetadata { - id: uuid::new_v4(), - op_type: op.type(), - timestamp: Utc::now(), - user: current_user(), - path: op.path(), - content_hash: Some(receipt.hash), - original_metadata: Some(op.original_metadata()), - // Everything needed for reversal - } -} ----- - -=== Phase 4: Execute - -Only after all safety measures are in place: - -[source,rust] ----- -fn execute(op: Operation) -> Result<()> { - // At this point, reversal is guaranteed possible - op.perform()?; - metadata_store.append(metadata)?; - Ok(()) -} ----- - -=== Phase 5: Reversal - -When undo is requested: - -[source,rust] ----- -fn undo(operation_id: &str) -> Result<()> { - let metadata = metadata_store.get(operation_id)?; - - // Retrieve original content - let content = content_store.retrieve(&metadata.content_hash)?; - - // Reconstruct inverse operation - let inverse = construct_inverse(&metadata, content); - - // Execute reversal - inverse.perform()?; - - // Record the undo - metadata_store.mark_undone(operation_id)?; - Ok(()) -} ----- - -== Operation-Specific RMR - -=== DELETE → CREATE - -[cols="1,1"] -|=== -| Forward (Delete) | Reverse (Create) - -| Read content → Store -| Retrieve content from store - -| Read metadata → Store -| Apply stored metadata - -| Remove file -| Write file with content - -| Log operation -| Log undo operation -|=== - -=== MODIFY → MODIFY - -[cols="1,1"] -|=== -| Forward (Modify) | Reverse (Modify back) - -| Read original content → Store -| Retrieve original content - -| Write new content -| Write original content back - -| Log with both hashes -| Log undo -|=== - -=== MOVE → MOVE - -[cols="1,1"] -|=== -| Forward (Move A→B) | Reverse (Move B→A) - -| Record source path -| Use stored source as destination - -| Record destination path -| Use stored destination as source - -| Perform rename -| Perform rename back - -| Log operation -| Log undo -|=== - -=== MKDIR → RMDIR - -[cols="1,1"] -|=== -| Forward (Mkdir) | Reverse (Rmdir) - -| Create directory -| Remove directory - -| Log path -| Use logged path - -| (No content to store) -| (No content to restore) -|=== - -=== RMDIR (recursive) → Restore - -[cols="1,1"] -|=== -| Forward (Rmdir -r) | Reverse (Full restore) - -| Walk directory tree -| Read manifest - -| Store each file content -| Restore each file - -| Store manifest -| Recreate directories - -| Remove directory tree -| Apply metadata -|=== - -== Formal Verification - -=== Theorem: RMR Correctness - -For all operations stem:[O] and states stem:[S]: - -[stem] -++++ -\text{undo}(\text{execute}(O, S)) = S -++++ - -==== Proof Sketch - -1. **Pre-capture completeness**: By construction, pre-capture stores all information needed to reconstruct original state. - -2. **Storage integrity**: Content-addressed storage with SHA256 verification ensures stored content is retrieved exactly. - -3. **Metadata sufficiency**: Operation metadata contains path, original metadata, and content hash—sufficient for reconstruction. - -4. **Inverse construction**: Each operation type has a defined inverse that uses stored information. - -5. **Composition**: Undo operations are themselves operations that can be tracked. ∎ - -=== Theorem: Sequential Reversibility (Theorem 3.4) - -For operations stem:[O_1, O_2, \ldots, O_n]: - -[stem] -++++ -\text{undo}(O_n) \circ \cdots \circ \text{undo}(O_1) \circ O_1 \circ \cdots \circ O_n(S) = S -++++ - -==== Proof - -By induction on stem:[n]: - -**Base case** (stem:[n=1]): Direct application of RMR correctness. - -**Inductive step**: Assume true for stem:[n-1]. Then: -[stem] -++++ -\text{undo}(O_n)(O_n(O_{n-1}(\cdots O_1(S)))) = O_{n-1}(\cdots O_1(S)) -++++ - -By inductive hypothesis, the remaining undos restore stem:[S]. ∎ - -== Implementation Details - -=== Content Store - -[source,rust] ----- -pub struct ContentStore { - root: PathBuf, - compression: bool, -} - -impl ContentStore { - // Store content, return hash - pub fn store(&self, content: &[u8]) -> Result { - let hash = ContentHash::from_bytes(content); - let path = self.content_path(&hash); - - // Deduplication: skip if already stored - if path.exists() { - return Ok(hash); - } - - // Write (optionally compressed) - self.write_content(&path, content)?; - Ok(hash) - } - - // Retrieve with integrity verification - pub fn retrieve(&self, hash: &ContentHash) -> Result> { - let content = self.read_content(&self.content_path(hash))?; - - // Verify integrity - if !hash.verify(&content) { - return Err(JanusError::ContentIntegrityError); - } - - Ok(content) - } -} ----- - -=== Metadata Store - -[source,rust] ----- -pub struct MetadataStore { - path: PathBuf, - log: OperationLog, -} - -impl MetadataStore { - // Append-only operation logging - pub fn append(&mut self, metadata: OperationMetadata) -> Result<()> { - self.log.operations.push(metadata); - self.save()?; - Ok(()) - } - - // Mark operation as undone - pub fn mark_undone(&mut self, id: &str, undo_id: &str) -> Result<()> { - if let Some(op) = self.get_mut(id) { - op.undone = true; - op.undo_operation_id = Some(undo_id.to_string()); - self.save()?; - } - Ok(()) - } -} ----- - -== Performance Characteristics - -|=== -| Metric | Value | Notes - -| Storage overhead -| O(content size) -| With deduplication - -| Capture latency -| O(file size) -| Single read operation - -| Undo latency -| O(file size) -| Single write operation - -| Metadata overhead -| ~500 bytes/operation -| JSON serialized -|=== - -== Limitations - -1. **Requires disk space**: Original content must be stored -2. **Not instantaneous**: Pre-capture adds latency to operations -3. **Single-system**: RMR operates on local file system only -4. **No external side effects**: Cannot reverse network operations, etc. - -== Further Reading - -* link:rmo-primitive.adoc[RMO: Obliterative Wipe Primitive] -* link:formal-model.adoc[Formal Model of File System State] -* link:../architecture/content-store.adoc[Content Store Implementation] diff --git a/docs/wiki/theory/rmr-primitive.md b/docs/wiki/theory/rmr-primitive.md new file mode 100644 index 0000000..18130a8 --- /dev/null +++ b/docs/wiki/theory/rmr-primitive.md @@ -0,0 +1,312 @@ + + + +# RMR: Reversible Transaction Primitive + +## Definition + +**RMR (Reversible Transaction)** is a data primitive that guarantees the existence of a mathematically sound inverse function for every state change. + +> For every operation $F$ applied to state $S$, there exists an inverse $F^{-1}$ such that $F^{-1}(F(S)) = S$. + +## Core Properties + +### Property 1: Deterministic Reversal + +Given an operation and its metadata, the reversal is uniquely determined: + +$$\text{undo}: (\text{Operation}, \text{Metadata}) \to \text{Operation}^{-1}$$ + +There is no ambiguity in how to reverse an operation. + +### Property 2: State Preservation + +The reversal restores *complete* original state, including: + +- File content (byte-for-byte identical) + +- File metadata (permissions, ownership, timestamps) + +- Directory structure + +- Symbolic link targets + +### Property 3: Sequential Composition + +For a sequence of operations, reversals compose correctly: + +$$(O_2 \circ O_1)^{-1} = O_1^{-1} \circ O_2^{-1}$$ + +Operations are undone in reverse order. + +### Property 4: Transaction Atomicity + +Operations grouped in a transaction satisfy: + +- All-or-nothing execution + +- All-or-nothing reversal + +- Intermediate states are not externally visible + +## RMR Protocol + +### Phase 1: Pre-Capture + +Before any modification, capture everything needed for reversal: + +``` rust +// For a DELETE operation +fn pre_capture_delete(path: &Path) -> PreCaptureData { + PreCaptureData { + content: fs::read(path), // Full file content + metadata: FileMetadata::from_path(path), // All metadata + content_hash: sha256(content), // For verification + } +} +``` + +### Phase 2: Secure Storage + +Store captured data in content-addressed storage: + +``` rust +fn secure_store(data: PreCaptureData) -> StorageReceipt { + let hash = content_store.store(data.content)?; + // Content is now safely stored with deduplication + StorageReceipt { hash, verified: true } +} +``` + +### Phase 3: Metadata Recording + +Create complete operation metadata: + +``` rust +fn record_metadata(op: Operation, receipt: StorageReceipt) -> OperationMetadata { + OperationMetadata { + id: uuid::new_v4(), + op_type: op.type(), + timestamp: Utc::now(), + user: current_user(), + path: op.path(), + content_hash: Some(receipt.hash), + original_metadata: Some(op.original_metadata()), + // Everything needed for reversal + } +} +``` + +### Phase 4: Execute + +Only after all safety measures are in place: + +``` rust +fn execute(op: Operation) -> Result<()> { + // At this point, reversal is guaranteed possible + op.perform()?; + metadata_store.append(metadata)?; + Ok(()) +} +``` + +### Phase 5: Reversal + +When undo is requested: + +``` rust +fn undo(operation_id: &str) -> Result<()> { + let metadata = metadata_store.get(operation_id)?; + + // Retrieve original content + let content = content_store.retrieve(&metadata.content_hash)?; + + // Reconstruct inverse operation + let inverse = construct_inverse(&metadata, content); + + // Execute reversal + inverse.perform()?; + + // Record the undo + metadata_store.mark_undone(operation_id)?; + Ok(()) +} +``` + +## Operation-Specific RMR + +### DELETE → CREATE + +| Forward (Delete) | Reverse (Create) | +|-----------------------|-----------------------------| +| Read content → Store | Retrieve content from store | +| Read metadata → Store | Apply stored metadata | +| Remove file | Write file with content | +| Log operation | Log undo operation | + +### MODIFY → MODIFY + +| Forward (Modify) | Reverse (Modify back) | +|-------------------------------|-----------------------------| +| Read original content → Store | Retrieve original content | +| Write new content | Write original content back | +| Log with both hashes | Log undo | + +### MOVE → MOVE + +| Forward (Move A→B) | Reverse (Move B→A) | +|-------------------------|----------------------------------| +| Record source path | Use stored source as destination | +| Record destination path | Use stored destination as source | +| Perform rename | Perform rename back | +| Log operation | Log undo | + +### MKDIR → RMDIR + +| Forward (Mkdir) | Reverse (Rmdir) | +|-----------------------|-------------------------| +| Create directory | Remove directory | +| Log path | Use logged path | +| (No content to store) | (No content to restore) | + +### RMDIR (recursive) → Restore + +| Forward (Rmdir -r) | Reverse (Full restore) | +|-------------------------|------------------------| +| Walk directory tree | Read manifest | +| Store each file content | Restore each file | +| Store manifest | Recreate directories | +| Remove directory tree | Apply metadata | + +## Formal Verification + +### Theorem: RMR Correctness + +For all operations $O$ and states $S$: + +$$\text{undo}(\text{execute}(O, S)) = S$$ + +#### Proof Sketch + +1. **Pre-capture completeness**: By construction, pre-capture stores all information needed to reconstruct original state. + +2. **Storage integrity**: Content-addressed storage with SHA256 verification ensures stored content is retrieved exactly. + +3. **Metadata sufficiency**: Operation metadata contains path, original metadata, and content hash—sufficient for reconstruction. + +4. **Inverse construction**: Each operation type has a defined inverse that uses stored information. + +5. **Composition**: Undo operations are themselves operations that can be tracked. ∎ + +### Theorem: Sequential Reversibility (Theorem 3.4) + +For operations $O_1, O_2, \ldots, O_n$: + +$$\text{undo}(O_n) \circ \cdots \circ \text{undo}(O_1) \circ O_1 \circ \cdots \circ O_n(S) = S$$ + +#### Proof + +By induction on $n$: + +**Base case** ($n=1$): Direct application of RMR correctness. + +**Inductive step**: Assume true for $n-1$. Then: + +$$\text{undo}(O_n)(O_n(O_{n-1}(\cdots O_1(S)))) = O_{n-1}(\cdots O_1(S))$$ + +By inductive hypothesis, the remaining undos restore $S$. ∎ + +## Implementation Details + +### Content Store + +``` rust +pub struct ContentStore { + root: PathBuf, + compression: bool, +} + +impl ContentStore { + // Store content, return hash + pub fn store(&self, content: &[u8]) -> Result { + let hash = ContentHash::from_bytes(content); + let path = self.content_path(&hash); + + // Deduplication: skip if already stored + if path.exists() { + return Ok(hash); + } + + // Write (optionally compressed) + self.write_content(&path, content)?; + Ok(hash) + } + + // Retrieve with integrity verification + pub fn retrieve(&self, hash: &ContentHash) -> Result> { + let content = self.read_content(&self.content_path(hash))?; + + // Verify integrity + if !hash.verify(&content) { + return Err(JanusError::ContentIntegrityError); + } + + Ok(content) + } +} +``` + +### Metadata Store + +``` rust +pub struct MetadataStore { + path: PathBuf, + log: OperationLog, +} + +impl MetadataStore { + // Append-only operation logging + pub fn append(&mut self, metadata: OperationMetadata) -> Result<()> { + self.log.operations.push(metadata); + self.save()?; + Ok(()) + } + + // Mark operation as undone + pub fn mark_undone(&mut self, id: &str, undo_id: &str) -> Result<()> { + if let Some(op) = self.get_mut(id) { + op.undone = true; + op.undo_operation_id = Some(undo_id.to_string()); + self.save()?; + } + Ok(()) + } +} +``` + +## Performance Characteristics + +| Metric | Value | Notes | +|-------------------|----------------------|------------------------| +| Storage overhead | O(content size) | With deduplication | +| Capture latency | O(file size) | Single read operation | +| Undo latency | O(file size) | Single write operation | +| Metadata overhead | ~500 bytes/operation | JSON serialized | + +## Limitations + +1. **Requires disk space**: Original content must be stored + +2. **Not instantaneous**: Pre-capture adds latency to operations + +3. **Single-system**: RMR operates on local file system only + +4. **No external side effects**: Cannot reverse network operations, etc. + +## Further Reading + +- [RMO: Obliterative Wipe Primitive](rmo-primitive.md) + +- [Formal Model of File System State](formal-model.md) + +- [Content Store Implementation](../architecture/content-store.md) From e4aaf1a2793e594d67fcebdae49c1353ccf1eca7 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 14 Oct 2026 00:49:57 +0000 Subject: [PATCH 4/4] docs(wiki): mark example shell paths as project-relative (Hypatia SD022) Hypatia's code scanning raised six SD022 "stale path after rename" warnings on #156: the wiki's example sessions (reorganising a project, a risky refactor) run `jk` against a user's own project, written as `src/components/` and the like. SD022 reads a bare `src//` as this repository's tree. `./src/...` is the same relative path to the shell, and SD022 leaves it alone because a path segment precedes `src/` (lib/rules/structural_drift.ex at hypatia 4be38c8). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Kjdf4Mf4Z41bNQh2TrsEp9 --- docs/wiki/examples/index.md | 12 ++++++------ docs/wiki/guides/transactions.md | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/wiki/examples/index.md b/docs/wiki/examples/index.md index c09eea2..90235b2 100644 --- a/docs/wiki/examples/index.md +++ b/docs/wiki/examples/index.md @@ -48,13 +48,13 @@ jk modify config.yaml --sed 's/log_level: INFO/log_level: DEBUG/' jk begin "reorganize project" # Move files to new structure -jk mkdir -p src/components -jk mkdir -p src/utils -jk mkdir -p src/services +jk mkdir -p ./src/components +jk mkdir -p ./src/utils +jk mkdir -p ./src/services -jk move components/* src/components/ -jk move utils/* src/utils/ -jk move services/* src/services/ +jk move components/* ./src/components/ +jk move utils/* ./src/utils/ +jk move services/* ./src/services/ # Preview the changes jk preview diff --git a/docs/wiki/guides/transactions.md b/docs/wiki/guides/transactions.md index 407c86d..6830671 100644 --- a/docs/wiki/guides/transactions.md +++ b/docs/wiki/guides/transactions.md @@ -196,9 +196,9 @@ jk begin "risky refactor" # jk rollback # Complex multi-step operation -jk move src/old_module/ src/legacy/ -jk delete src/deprecated/*.py -jk modify src/main.py --file new_main.py +jk move ./src/old_module/ ./src/legacy/ +jk delete ./src/deprecated/*.py +jk modify ./src/main.py --file new_main.py # Verify ./verify_integrity.sh