ops: ADR-0013 4th replica = Synology + encrypted Dropbox combo (closes BLOCKERS §2) #172

Closed
claude wants to merge 1 commit from claude-orchestrator/4th-replica-synology-encrypted-dropbox into main
Collaborator

What

Closes BLOCKERS_FOR_OPERATOR.md §2 with a two-tier 4th replica: Synology home NAS as the operator-owned local primary, encrypted Dropbox as the geographic offsite layer.

Beats pure Dropbox (per GPT-5.5 Pro recommendation) by adding operator-owned local primary while preserving cloud-cipher offsite durability. The operator's kip Synology (Tailnet 100.91.208.73, on-site home NAS) was previously unused for platform replication; this PR brings it into the chain.

Two-tier design

M1 (primary working copy)
  │
  ├── rsync ─────────► RS2000 /srv/baseline-mirror/  (datacenter, existing)
  ├── rsync ─────────► VPS1000 /srv/baseline-mirror/ (datacenter, existing)
  └── rsync ─────────► kip Synology /volume1/iskra-baseline/  (NEW, on-site home)
                              │
                              └── tar | age | rclone ──► Dropbox iskra-baseline-encrypted/
                                                          (NEW, geographic offsite, cipher-only)

5 replicas total. Cloud sees only cipher.

Files (8)

File Purpose
decisions/0013-4th-replica-synology-encrypted-dropbox.md The ADR
scripts/4th-replica/setup-synology.sh Interactive one-time setup
scripts/4th-replica/sync-to-synology.sh Daily M1 → Synology rsync
scripts/4th-replica/sync-synology-to-dropbox.sh Weekly tar→age→rclone pipeline
scripts/restore_check.sh Monthly restore drill
docs/runbooks/4th-replica.md Operator runbook
BLOCKERS_FOR_OPERATOR.md §2 Amended: closed via ADR-0013

All scripts bash -n clean. All scripts chmod +x.

Encryption design

  • age (X25519) with operator-held key at ~/.config/iskra-baseline/age-key.txt
  • Key backed up: passphrase-encrypted file + Infisical (post-Phase 05) + secondary safe location
  • Cloud (Dropbox) sees opaque ciphertext only
  • Key NEVER in agent context (claude/codex never see plaintext or key bytes per ADR-0010 wrapper pattern)

Restore acceptance criteria

  • RTO target: 4h
  • RPO target: 24h
  • Monthly drill (scripts/restore_check.sh):
    • Restore from Synology to scratch dir
    • Size diff <1%
    • File count match
    • 10 random spot-check sha256 match
    • <30 min over LAN

Failure scenarios matrix (per runbook)

Scenario Synology Dropbox Recovery
M1 dies preserved preserved restore from Synology over LAN (fast)
M1 + Synology die (fire/burglary) LOST preserved restore from encrypted Dropbox + age key
age key lost preserved UNUSABLE restore from Synology only
Dropbox compromised preserved cipher visible (useless without key) rotate age key + delete Dropbox blobs
Tailnet down unreachable locally reachable restore from Dropbox over public internet (age key required)

Defense in depth: 5 replicas, multiple independent failure modes required for total loss.

What this PR DOES NOT do

  • Does NOT auto-execute setup (operator runs scripts/4th-replica/setup-synology.sh interactively after merge).
  • Does NOT generate age key for operator (script prompts; operator runs key-gen step).
  • Does NOT include launchd plists for scheduling (separate Phase 07 ticket).
  • Does NOT integrate with attention-dispatcher (Phase 07 ticket F; state files at ~/.local/share/iskra/4th-replica/*.json are dispatcher-pollable).
  • Does NOT touch any existing replica or sacred path.

Acceptance criteria

  • ADR-0013 has Status: Proposed.
  • All 4 scripts are executable + bash -n clean.
  • Runbook covers setup + sync + restore (from both Synology AND Dropbox) + age rotation + failure matrix + env vars + Phase 07 integration.
  • BLOCKERS §2 amended (old options retained for context, marked rejected).
  • 3+3 canary fires on risk/exposure (touches credentials handling indirectly via age key).
  • Operator runs setup-synology.sh post-merge → first sync succeeds → first restore drill passes.

Rollback

git revert <merge-commit>
git push origin main
ssh kip 'rm -rf /volume1/iskra-baseline'    # only if you want to remove created share
rclone delete iskra-dropbox-encrypted:iskra-baseline-encrypted/  # only if you want to delete cipher blobs

Single commit, 7 net-new files + 1 edit. Reverting deletes scripts/runbook/ADR; cleaning Synology + Dropbox is operator-discretionary.

Refs

  • GPT-5.5 Pro oracle review 2026-05-11 §4 OPEN DECISIONS (4th replica)
  • BLOCKERS_FOR_OPERATOR.md §2 (existing blocker, this PR closes)
  • Operator's kip Synology Tailnet audit 2026-05-11
  • ADR-0010 §6 (age key never in agent context)
  • Companion: PR #168, #169, #170, #171

Codex effort needed

Review + merge. Operator runs scripts/4th-replica/setup-synology.sh interactively after merge (one-time setup, ~15 min including rclone OAuth). After that, scripts are scheduled-cron-driven (Phase 07 launchd ticket follows).


Role: orchestrator / drafter (claude)
Lane: ops / 4th replica decision
Next: operator merges → runs setup → first daily sync to Synology → first weekly Dropbox encrypted sync → first monthly restore drill.

## What Closes `BLOCKERS_FOR_OPERATOR.md` §2 with a two-tier 4th replica: **Synology home NAS as the operator-owned local primary, encrypted Dropbox as the geographic offsite layer**. Beats pure Dropbox (per GPT-5.5 Pro recommendation) by adding operator-owned local primary while preserving cloud-cipher offsite durability. The operator's `kip` Synology (Tailnet 100.91.208.73, on-site home NAS) was previously unused for platform replication; this PR brings it into the chain. ## Two-tier design ``` M1 (primary working copy) │ ├── rsync ─────────► RS2000 /srv/baseline-mirror/ (datacenter, existing) ├── rsync ─────────► VPS1000 /srv/baseline-mirror/ (datacenter, existing) └── rsync ─────────► kip Synology /volume1/iskra-baseline/ (NEW, on-site home) │ └── tar | age | rclone ──► Dropbox iskra-baseline-encrypted/ (NEW, geographic offsite, cipher-only) ``` 5 replicas total. Cloud sees only cipher. ## Files (8) | File | Purpose | |------|---------| | `decisions/0013-4th-replica-synology-encrypted-dropbox.md` | The ADR | | `scripts/4th-replica/setup-synology.sh` | Interactive one-time setup | | `scripts/4th-replica/sync-to-synology.sh` | Daily M1 → Synology rsync | | `scripts/4th-replica/sync-synology-to-dropbox.sh` | Weekly tar→age→rclone pipeline | | `scripts/restore_check.sh` | Monthly restore drill | | `docs/runbooks/4th-replica.md` | Operator runbook | | `BLOCKERS_FOR_OPERATOR.md` §2 | Amended: closed via ADR-0013 | All scripts `bash -n` clean. All scripts `chmod +x`. ## Encryption design - `age` (X25519) with operator-held key at `~/.config/iskra-baseline/age-key.txt` - Key backed up: passphrase-encrypted file + Infisical (post-Phase 05) + secondary safe location - Cloud (Dropbox) sees opaque ciphertext only - Key NEVER in agent context (claude/codex never see plaintext or key bytes per ADR-0010 wrapper pattern) ## Restore acceptance criteria - **RTO target**: 4h - **RPO target**: 24h - **Monthly drill** (`scripts/restore_check.sh`): - Restore from Synology to scratch dir - Size diff <1% - File count match - 10 random spot-check sha256 match - <30 min over LAN ## Failure scenarios matrix (per runbook) | Scenario | Synology | Dropbox | Recovery | |----------|----------|---------|----------| | M1 dies | preserved | preserved | restore from Synology over LAN (fast) | | M1 + Synology die (fire/burglary) | LOST | preserved | restore from encrypted Dropbox + age key | | age key lost | preserved | UNUSABLE | restore from Synology only | | Dropbox compromised | preserved | cipher visible (useless without key) | rotate age key + delete Dropbox blobs | | Tailnet down | unreachable locally | reachable | restore from Dropbox over public internet (age key required) | Defense in depth: 5 replicas, multiple independent failure modes required for total loss. ## What this PR DOES NOT do - Does NOT auto-execute setup (operator runs `scripts/4th-replica/setup-synology.sh` interactively after merge). - Does NOT generate age key for operator (script prompts; operator runs key-gen step). - Does NOT include launchd plists for scheduling (separate Phase 07 ticket). - Does NOT integrate with attention-dispatcher (Phase 07 ticket F; state files at `~/.local/share/iskra/4th-replica/*.json` are dispatcher-pollable). - Does NOT touch any existing replica or sacred path. ## Acceptance criteria - [x] ADR-0013 has Status: Proposed. - [x] All 4 scripts are executable + `bash -n` clean. - [x] Runbook covers setup + sync + restore (from both Synology AND Dropbox) + age rotation + failure matrix + env vars + Phase 07 integration. - [x] BLOCKERS §2 amended (old options retained for context, marked rejected). - [ ] 3+3 canary fires on `risk/exposure` (touches credentials handling indirectly via age key). - [ ] Operator runs `setup-synology.sh` post-merge → first sync succeeds → first restore drill passes. ## Rollback ``` git revert <merge-commit> git push origin main ssh kip 'rm -rf /volume1/iskra-baseline' # only if you want to remove created share rclone delete iskra-dropbox-encrypted:iskra-baseline-encrypted/ # only if you want to delete cipher blobs ``` Single commit, 7 net-new files + 1 edit. Reverting deletes scripts/runbook/ADR; cleaning Synology + Dropbox is operator-discretionary. ## Refs - GPT-5.5 Pro oracle review 2026-05-11 §4 OPEN DECISIONS (4th replica) - BLOCKERS_FOR_OPERATOR.md §2 (existing blocker, this PR closes) - Operator's `kip` Synology Tailnet audit 2026-05-11 - ADR-0010 §6 (age key never in agent context) - Companion: PR #168, #169, #170, #171 ## Codex effort needed **Review + merge.** Operator runs `scripts/4th-replica/setup-synology.sh` interactively after merge (one-time setup, ~15 min including rclone OAuth). After that, scripts are scheduled-cron-driven (Phase 07 launchd ticket follows). --- **Role:** orchestrator / drafter (claude) **Lane:** ops / 4th replica decision **Next:** operator merges → runs setup → first daily sync to Synology → first weekly Dropbox encrypted sync → first monthly restore drill.
ops: ADR-0013 4th replica = Synology + encrypted Dropbox combo (closes BLOCKERS §2)
All checks were successful
canary-required / collect-diff (pull_request) Successful in 3s
canary-required / canary (pull_request) Successful in 12s
58efe28a36
Two-tier 4th replica:
- Synology home NAS (operator-owned, on Tailnet) — primary local 4th replica
- Encrypted Dropbox blob (cipher-only via age) — geographic offsite

Beats pure Dropbox (per GPT recommendation) by adding operator-owned local
primary while preserving cloud-cipher offsite durability.

Files (8)

- decisions/0013-4th-replica-synology-encrypted-dropbox.md — the ADR
- scripts/4th-replica/setup-synology.sh — interactive one-time setup
  (verifies kip Tailnet reachable, creates iskra-baseline share, generates
  age key, configures rclone Dropbox remote)
- scripts/4th-replica/sync-to-synology.sh — daily M1 -> Synology rsync
  (--delete canonical mirror); state file at ~/.local/share/iskra/4th-replica/last-sync.json
- scripts/4th-replica/sync-synology-to-dropbox.sh — weekly tar | age | rclone
  pipeline; rolling window of 8 weekly backups on Dropbox
- scripts/restore_check.sh — monthly restore drill (rsync from Synology
  to scratch, size+count+10-checksum spot check, <30min target)
- docs/runbooks/4th-replica.md — operator runbook (setup, sync, restore
  procedures from Synology AND from encrypted Dropbox, age key rotation,
  failure scenarios matrix)
- BLOCKERS_FOR_OPERATOR.md §2 amended: closed via ADR-0013

Encryption design

- age (X25519) with operator-held key at ~/.config/iskra-baseline/age-key.txt
- Key backed up: passphrase-encrypted file + Infisical (post-Phase 05) +
  secondary safe location
- Cloud (Dropbox) sees opaque ciphertext only
- Key NEVER in agent context (Claude/Codex never see plaintext or key bytes)

Restore acceptance criteria

- RTO 4h, RPO 24h (per GPT recommendation)
- Monthly drill via scripts/restore_check.sh:
  - Restore from Synology to scratch dir
  - Size diff <1%
  - File count match
  - 10 random spot-check sha256 match
  - <30 min over LAN

Defense in depth: Synology fast restore + Dropbox geographic durability.
Both lost only if fire/burglary AND Dropbox account compromised AND age key
lost — all three independent failures.

What this PR DOES NOT

- Does NOT auto-execute setup (operator runs setup-synology.sh manually)
- Does NOT generate age key for operator (operator runs key-gen prompted by setup)
- Does NOT include launchd plists (separate Phase 07 ticket)
- Does NOT integrate with attention-dispatcher (Phase 07 ticket F)
- Does NOT touch existing replicas or sacred paths

Refs

- GPT-5.5 Pro oracle review 2026-05-11 §4 OPEN DECISIONS (4th replica)
- BLOCKERS_FOR_OPERATOR.md §2 (existing blocker; this PR closes)
- Operator's kip Synology audit (Tailnet 100.91.208.73, on-site home NAS)
- Phase 07 design F (attention-dispatcher) will integrate with state files

**Role:** orchestrator / drafter (claude)
Collaborator

Fork A triage (codex): closing as rewrite-needed under the current roadmap.

The 4th-replica/Synology+encrypted-Dropbox idea belongs, if still desired, in Milestone 02 (DR and restore confidence). This PR predates the cutoff roadmap, references BLOCKERS_FOR_OPERATOR.md, and ships scripts/docs against the old baseline/replica framing.

Recommendation: reopen as a fresh DR/restore-confidence PR or issue after #45/#238 planning, with current paths, current restore acceptance criteria, and no deprecated operator-status file edits.

Fork A triage (codex): closing as rewrite-needed under the current roadmap. The 4th-replica/Synology+encrypted-Dropbox idea belongs, if still desired, in Milestone 02 (DR and restore confidence). This PR predates the cutoff roadmap, references `BLOCKERS_FOR_OPERATOR.md`, and ships scripts/docs against the old baseline/replica framing. Recommendation: reopen as a fresh DR/restore-confidence PR or issue after #45/#238 planning, with current paths, current restore acceptance criteria, and no deprecated operator-status file edits.
codex closed this pull request 2026-05-24 07:59:22 +02:00
Some checks are pending
canary-required / collect-diff (pull_request) Successful in 3s
canary-required / canary (pull_request) Successful in 12s
base-is-main / guard (pull_request)
Required
patchwarden-pr-sanity / sanity (pull_request)
Required

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
W6d-automerge-calibration
agent/claude-code
agent/codex
agent/hermes
agent/iskra
agent/ollama
agent/patchwarden
automerge-candidate
class/security-sensitive
cutover-gate
dependency/blocked
dependency/blocks-others
dependency/cross-repo
dependency/needs-confirmation
domain:agents
domain:ci
domain:docs
domain:forgejo
domain:infra
domain:memory
domain:runtime
domain:signal
domain:ux
flow/architecture
flow/blocked
flow/deployed
flow/done
flow/implementation
flow/intake
flow/maintained
flow/observed
flow/ready
flow/refining
flow/retired
flow/review
iterating
judge/codex-candidate
judge/hermes-candidate
judge/low-confidence
judge/needs-refinement
judge/operator-needed
judge/p0
judge/p1
judge/p2
judge/p3
judge/park
judge/patchwarden-candidate
judge/stale-priority
kind/adr
kind/bug
kind/chore
kind/feature
kind/infra
kind/ops
kind/refactor
kind/research
large-impact
merge/auto
merge/manual
merge/manual-dependency-conflict
merge/manual-failing-tests
merge/manual-merge-conflict
merge/manual-missing-review
merge/manual-operator-preference
merge/manual-red-zone
merge/manual-security-sensitive
merge/manual-unclear-scope
merge/manual-unknown
meta
mode:operator-only
mode:patchwarden-iskra-approved
mode:safe-auto
needs-operator-decision
needs-triage
not-ready
observed/erroring
observed/needs-followup
observed/pending
observed/retire-candidate
observed/unused
observed/used
operator-emotional
owner-attention
phase/02
phase/03
priority:p0
priority:p1
priority:p2
priority:p3
proposed
ready-for-agent
ready-for-operator
recovery
review:claude-reviewed
review:codex-reviewed
review:dziadek-reviewed
review:needs-human
risk/exposure
risk/process
risk/product
risk/runtime
safety:external-write
safety:no-prod-mutation
safety:prod-impact
safety:secret-touch
size/large
size/medium
size/small
size/tiny
size/unknown
source/adr
source/agent-generated
source/manual
source/operator-chat
source/voice-note
status:blocked
status:codex-ready
status:merged:pending-evidence
status:needs-evidence
status:operator-needed
status:parked
tier/full
tier/lite
tier/stacked
tier:0-platform-substrate
tier:1-iskra-value-layer
tier:2-tools-products-modules
type:bug
type:chore
type:docs
type:feat
type:policy
type:research
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
pdurlej/platform!172
No description provided.