Some checks failed
nigig-site / Owned paths and honest test contracts (push) Has been cancelled
nigig-site / Cargo check-all-targets (push) Has been cancelled
nigig-site / Cargo clippy-site-owned (push) Has been cancelled
nigig-site / Cargo contained-media-export-fixtures (push) Has been cancelled
nigig-site / Cargo containment-storage-crypto (push) Has been cancelled
nigig-site / Cargo integration-non-live (push) Has been cancelled
nigig-site / Cargo production-dependency-containment (push) Has been cancelled
nigig-site / Cargo unit (push) Has been cancelled
nigig-site / Runtime UI (explicitly skipped until enabled) (push) Has been cancelled
nigig-site / Migration and recovery (explicitly skipped until enabled) (push) Has been cancelled
nigig-site / Media limits (explicitly skipped until enabled) (push) Has been cancelled
nigig-site / Real server interoperability (explicitly skipped until enabled) (push) Has been cancelled
nigig-site / Security and supply-chain baseline (push) Has been cancelled
nigig-site / Release capability gate (push) Has been cancelled
repo hygiene / hygiene (push) Has been cancelled
819 lines
53 KiB
Markdown
819 lines
53 KiB
Markdown
# nigig-site — Security and Reliability Remediation Plan
|
||
|
||
**Plan date:** 2026-09-12
|
||
**Audit baseline:** `e899a271c69efca0e11ae274b879378d2f26485f` (`main`)
|
||
**Scope:** `crates/apps/nigig-site`, its domain/storage/media/export code, and its protocol boundary with `nimanyatta`
|
||
**Status:** Execution in progress. SITE-00 and SITE-01 are complete and published; SITE-02 through SITE-19 remain incomplete.
|
||
**Published evidence:** SITE-00 `f49d8b16ac8f1245ba1e80ac19588825f91a79f4`; SITE-01 `5d2d890f701444a3ff7e9c227e41e4608406ee89`.
|
||
**Release posture:** **Do not enable current multi-device sync. Do not call current storage confidential. Do not claim real OCR, safe media processing, or lossless photo reports.**
|
||
|
||
This is the authoritative forward execution plan for this crate. `README.md` and `NIGIG_SITE_ASSESSMENT_AND_PLAN.md` contain useful history but do not satisfy the security, migration, and release gates here.
|
||
|
||
---
|
||
|
||
## 1. Executive verdict
|
||
|
||
`nigig-site` handles high-risk construction and identity data—worker ID numbers, names, phones, client/site details, reports, meetings, approvals, locations, photos, and chat—but its storage and sync designs are not safe for that data.
|
||
|
||
The two most urgent defects are destructive storage recovery and plaintext whole-store sync:
|
||
|
||
- If the keystore is unavailable, saving falls back to plaintext by design.
|
||
- If the data key is missing, a new key can be created, decryption fails, the file is classified as corrupt, and the app backs it up then seeds a demo site. Tampering, key loss, unsupported data, and JSON corruption all collapse into the same destructive path.
|
||
- Save APIs return no failure, discard write/sync/rename errors, queue unbounded full-store clones, and production never drains the queue on shutdown.
|
||
- Sync serializes the entire multi-site store—including worker identity/contact data—into ordinary text messages in the selected site's normal chat room. HTTPS protects a network hop; it does not hide plaintext from the chat server, room members, logs, backups, or moderation systems.
|
||
- Snapshot chunks split raw UTF-8, grow after JSON escaping, have no encoded/decompressed size contract, integrity, trusted sender identity, site binding, replay defense, tombstones, or reliable concurrent request correlation.
|
||
|
||
The rest of the app repeats the same architectural pattern: public mutable structs and screen-level code bypass central authorization, state transitions, reference validation, audit history, finite numeric rules, and site scoping. Media processing trusts stored paths, decodes before limits, buffers every frame repeatedly, and writes collision-prone files non-atomically. OCR does no text recognition. PDF/DOCX print photo paths instead of images, contain Unicode panic paths, and DOCX assembly unwraps production errors. AI constructs Claude while labelling it OpenAI, can accept a timed-out partial response as success, and has no request IDs.
|
||
|
||
**Current grade:** security F, privacy F, data durability F, sync correctness F, domain integrity D-, media safety F, export fidelity D-, architecture D, test credibility D+.
|
||
|
||
---
|
||
|
||
## 2. Evidence and reproducible baseline
|
||
|
||
### 2.1 Observed validation
|
||
|
||
| Check | Result at baseline | Honest interpretation |
|
||
|---|---:|---|
|
||
| `cargo check --locked -p nigig-site` | **Passed** | Compilation only. |
|
||
| `cargo test --locked -p nigig-site` | **Inconclusive; rustc was killed by signal 9 while compiling `makepad-widgets`** | No suite result may be claimed. Retry in low-memory CI with one job and stripped dev/test debug data. |
|
||
| Site-specific Forgejo workflow | **Absent** | Security and lifecycle regressions have no crate-owned CI. |
|
||
| `tests/sync_e2e.rs` without `NIMANYATTA_E2E_URL` | Returns success without testing | A silent early return is not an executed skip or pass. |
|
||
| In-repository `nimanyatta` server | **Absent; only a README stub exists** | App/server protocol cannot be reproduced from this repository. |
|
||
|
||
### 2.2 Primary evidence locations
|
||
|
||
- `src/store.rs`, `src/crypto.rs`: destructive load fallback, plaintext downgrade, swallowed write errors, unbounded snapshot queue, global whole-store lock/cache.
|
||
- `src/sync.rs`, `src/nimanyatta_client.rs`, `tests/sync_e2e.rs`, `crates/nimanyatta/README.md`: cross-site plaintext snapshots, unsafe chunking/merge/replay, singleton pending slots, protocol drift, absent server.
|
||
- `src/domain/{site,daily_report,workers,procurement,approvals,meetings}.rs`: stringly IDs, unrestricted mutable states, missing role/audit/reference/finite/date invariants, PII duplication.
|
||
- `src/site_frame/screens/*.rs`: implicit first-site fallback, stale/local caches, direct global mutations, broad disclosure, dead or weakly correlated actions.
|
||
- `src/{gif,video,ocr,report_pdf,doc_export,ai_refine,scheduler}.rs`: unbounded decode/encode, fake OCR, lossy exports, Unicode panic, provider/cancellation defects, and reminder ordering/time-zone defects.
|
||
|
||
---
|
||
|
||
## 3. Immediate threat statement
|
||
|
||
### Protected data
|
||
|
||
Worker identity numbers and images; names, phone/email; exact site/location data; attendance; reports and photographs; approvals; procurement/supplier data; meetings/transcripts; chat; credentials; device/site encryption keys; audit history.
|
||
|
||
### Adversaries and failures in scope
|
||
|
||
- Curious or compromised sync/chat server and its operators/backups/logs.
|
||
- Unauthorized or removed room/site member.
|
||
- Malicious member/device sending forged, replayed, oversized, future-dated, conflicting, or malformed events.
|
||
- Network attacker where TLS validation/configuration is weakened.
|
||
- Lost/stolen device and copied local files.
|
||
- Corrupt filesystem, disk full, power loss, process crash, missing/locked keystore, and downgrade/future-version binaries.
|
||
- Crafted images, text, PDFs/DOCX inputs/options, and sync payloads intended to exhaust resources or trigger panics.
|
||
- Normal concurrent use, out-of-order responses, project/site switching, and stale background jobs.
|
||
|
||
### Explicit limitation
|
||
|
||
End-to-end encryption cannot prevent an authorized endpoint from copying data, and a malicious server can withhold/reorder traffic. Availability and endpoint malware are not solved cryptographically. These limitations must appear in product documentation.
|
||
|
||
---
|
||
|
||
## 4. Non-negotiable invariants
|
||
|
||
1. **No plaintext downgrade.** If confidential persistence cannot encrypt, mutation is blocked or retained only in an explicitly volatile safe mode. It is never silently/plainly persisted.
|
||
2. **No destructive recovery.** Missing key, authentication failure, corrupt payload, unsupported version, I/O error, and valid empty are distinct states. None seeds demo data over user state.
|
||
3. **Durable success only.** Save success is a `Result` after serialization, encryption, write, flush, sync, atomic replace, and manifest/directory durability as supported.
|
||
4. **Explicit site context.** Every domain command, query, export, asset, reminder, and sync event takes typed `SiteId`; no “selected or first” fallback authorizes data access.
|
||
5. **Central commands and authorization.** Screens cannot mutate workflow fields directly. Identity, site role, transition, reference, finite value, revision, and audit checks occur in one service boundary.
|
||
6. **Data minimization.** Sync/export/AI/OCR receives only named required records/fields for one site and operation. The process-wide store is never serialized into a room.
|
||
7. **Authenticated site-scoped E2EE.** Server sees ciphertext and necessary routing metadata only. Envelopes bind protocol/version/site/key epoch/sender/sequence/content and reject forgery/replay/cross-site substitution.
|
||
8. **One shared protocol implementation.** App, server, and tests import the same typed protocol crate and endpoint semantics.
|
||
9. **Convergent explicit replication.** Edits and deletes replicate via operation IDs/revisions/tombstones and declared conflict rules. Attacker-controlled wall-clock strings do not decide authority.
|
||
10. **Correlated async work.** Every request/result has operation ID, site ID, base revision, and cancellation status. Stale results cannot mutate current state.
|
||
11. **Owned assets, not arbitrary paths.** Domain records refer to `AssetId`. Ingestion validates/copies bytes into a site-scoped managed store before decode/use.
|
||
12. **Bound before allocation.** Encoded bytes, dimensions, pixels, frames, decompressed bytes, text, pages, output, recursion, and queue depths are checked with overflow-safe arithmetic.
|
||
13. **No partial success.** AI timeout, media truncation, export failure, sync subset, and notification registration failure are explicit states.
|
||
14. **PII lifecycle is enforceable.** Access, purpose, retention, deletion/anonymization, backup, export, and audit rules exist for identity data and images.
|
||
15. **Demo data is opt-in.** Fixtures are visibly marked and created only by an explicit development/demo action.
|
||
|
||
---
|
||
|
||
## 5. Severity-ranked findings
|
||
|
||
### P0 — emergency release blockers
|
||
|
||
| ID | Finding | Concrete impact |
|
||
|---|---|---|
|
||
| SITE-P0-01 | `crypto::seal` falls back to a plaintext envelope when keyring/encryption is unavailable. | Worker/site PII is stored unencrypted while the app continues operating. |
|
||
| SITE-P0-02 | `load_or_create_dek` may create a new key for an existing encrypted store; any decrypt/parse failure then enters backup-and-reseed. | Key loss/tampering can make user data disappear from the app and be replaced by a demo site. |
|
||
| SITE-P0-03 | `save`/`save_to` discard directory, serialization, encryption fallback, write, sync, permission, rename, and directory-sync errors. | UI/state can report success although no durable update exists. |
|
||
| SITE-P0-04 | Async persistence uses an unbounded channel of full `SiteStore` clones and is never drained on production exit. | Memory amplification and lost final writes. |
|
||
| SITE-P0-05 | Sync puts the complete multi-site plaintext store into one site's ordinary chat room. | Cross-site disclosure of identity, contacts, reports, meetings, procurement, reminders, and chat to server/room members. |
|
||
| SITE-P0-06 | Sync envelopes have no cryptographic authenticity, site binding, trusted device identity, sequence/replay control, or decompression/total size limit. | Injection, replay, cross-site substitution, memory denial of service, and state poisoning. |
|
||
| SITE-P0-07 | Raw JSON is split at arbitrary bytes then lossy-converted to UTF-8; escaped envelope size can exceed transport limits; serialization failure becomes `{}`. | Corrupt/incomplete snapshots can look valid; Unicode and transport limits are violated. |
|
||
| SITE-P0-08 | Lexically greatest attacker-chosen snapshot ID wins; LWW accepts arbitrary future `updated_at`; add-only merges cannot propagate most edits/deletes. | Permanent eclipsing/poisoning and non-convergent devices. |
|
||
| SITE-P0-09 | Static Makepad request IDs plus singleton `PENDING_*` slots correlate concurrent room/send/fetch operations. | A response can be attributed to the wrong site/room; concurrent messages overwrite pending context. |
|
||
| SITE-P0-10 | AI timeout returns nonempty partial output as success and result events lack request/site/revision identity. | Partial or stale sensitive text overwrites a newer field/site. |
|
||
| SITE-P0-11 | Image/GIF/video paths decode arbitrary supplied files before pixel/byte limits and buffer every frame/copy. | Path abuse, decompression bombs, process OOM, and UI/job starvation. |
|
||
| SITE-P0-12 | AVI size/rate/count casts and multiplication are unchecked; all output is accumulated in memory. | Truncated headers, overflow, corrupt video, or memory exhaustion. |
|
||
|
||
### P1 — major security, correctness, and privacy defects
|
||
|
||
| ID | Finding | Consequence |
|
||
|---|---|---|
|
||
| SITE-P1-01 | Domain IDs are aliases to `String`; parent/site/supplier/meeting/worker references are not centrally validated. | Cross-site/dangling references and collisions survive into exports/sync. |
|
||
| SITE-P1-02 | Report/task/inspection/meeting/site status fields are publicly mutable; roles and legal transition order are not enforced. | Anyone/path can approve, inspect, complete, reopen, or rewrite history. |
|
||
| SITE-P1-03 | No immutable audit event records actor, reason, revision, device, and prior/new state. | Approval and PII actions are not accountable. |
|
||
| SITE-P1-04 | Worker scans duplicate complete ID numbers/names/phones into daily tables and expose them broadly. Captured ID images have no retention/delete lifecycle. | Excessive PII replication and disclosure. |
|
||
| SITE-P1-05 | Quantities, latitude/longitude, dates, durations, IDs, and uniqueness accept invalid/non-finite/inconsistent values. | NaN output, impossible schedules/locations, duplicate reports/scans, and invalid references. |
|
||
| SITE-P1-06 | Implicit first-site fallback substitutes another site when selection is absent/stale. | Wrong-site mutation/export/disclosure. |
|
||
| SITE-P1-07 | OCR performs corner detection/enhancement only and always returns no recognized text. | Product wording/integration overstates ID scanning capability. |
|
||
| SITE-P1-08 | PDF and DOCX print local media paths instead of embedding photos. | Reports are not portable and leak local filesystem structure. |
|
||
| SITE-P1-09 | PDF preview and meeting summary truncate UTF-8 strings with byte slicing. | Valid Unicode text can panic the app/export. |
|
||
| SITE-P1-10 | DOCX ZIP assembly uses production `unwrap()` and returns bytes instead of `Result`. | Recoverable file/codec failures crash and can lose unsaved work. |
|
||
| SITE-P1-11 | AI constructs `ClaudeApiChatProvider` with `ProviderKind::OpenAi`, uses environment credentials, and sends report text without a clear consent boundary. | Wrong provider behavior and undisclosed remote processing of site data. |
|
||
| SITE-P1-12 | Reminder fire time assumes fixed EAT regardless of site; invalid hour can panic; OS scheduling happens before durable state. | Wrong/duplicate/missing reminders and unrecoverable OS/app divergence. |
|
||
| SITE-P1-13 | Reminder is marked fired immediately after asynchronous post is spawned, even if delivery fails. | Missed reminders are recorded as delivered. |
|
||
| SITE-P1-14 | Sync E2E test silently returns success without a server and uses endpoint/method shapes that differ from the app. | CI claims nothing about real interoperability. |
|
||
|
||
### P2 — maintainability and performance debt
|
||
|
||
- One global clone-heavy store makes every mutation/write proportional to all sites and PII.
|
||
- Screens repeatedly clone whole state and cache derived site values without typed revision invalidation.
|
||
- Network code hand-parses loosely typed JSON and tolerates malformed success bodies as empty IDs/events.
|
||
- PDF/DOCX/media encoders accumulate full output and lack cancellation/metrics.
|
||
- Static/global environment manipulation makes tests serial, fragile, and unlike production configuration.
|
||
- No Site-owned workflow means dependency, security, test, and resource-limit regressions can merge unnoticed.
|
||
|
||
---
|
||
|
||
## 6. Target architecture
|
||
|
||
```text
|
||
SiteApp / Makepad screens
|
||
│ intents + explicit SiteContext
|
||
▼
|
||
SiteSessionController
|
||
├─ AuthSession { UserId, DeviceId, credentials/capabilities }
|
||
├─ SiteContext { SiteId, role/capabilities, revision }
|
||
├─ DomainCommandService (validation + authorization + audit)
|
||
├─ SiteRepository (encrypted, versioned, atomic, recoverable)
|
||
├─ AssetRepository (site-scoped, content-addressed, quota/retention)
|
||
├─ JobCoordinator (bounded, correlated, cancellable)
|
||
└─ SyncEngine
|
||
├─ local operation journal / tombstones
|
||
├─ shared typed protocol
|
||
├─ established/reviewed group key protocol
|
||
└─ authenticated encrypted transport envelopes
|
||
|
||
Server
|
||
├─ authentication and site membership/capabilities
|
||
├─ opaque encrypted event log + cursors + bounded snapshot blobs
|
||
└─ no application plaintext or client-side key material
|
||
```
|
||
|
||
### Persistence layout
|
||
|
||
```text
|
||
nigig-site/
|
||
profile/manifest # store ID, schema, key IDs; no PII plaintext
|
||
sites/<site-id>/manifest # encrypted aggregate pointers/revisions
|
||
sites/<site-id>/aggregates/... # encrypted envelopes
|
||
sites/<site-id>/journal/... # encrypted local operations
|
||
sites/<site-id>/assets/<id> # encrypted or platform-protected asset blobs
|
||
recovery/... # encrypted/preserved originals; never demo state
|
||
```
|
||
|
||
Selection and UI preferences are device-local metadata and are not replicated as domain state.
|
||
|
||
---
|
||
|
||
## 7. Resource and performance budgets
|
||
|
||
These are initial hard safety ceilings. Lower platform-specific quotas are allowed; unbounded operation is not.
|
||
|
||
| Resource | Initial limit | Enforcement point |
|
||
|---|---:|---|
|
||
| Legacy whole-store input | 64 MiB | Before read-to-end/decrypt/decode. |
|
||
| One aggregate plaintext | 16 MiB | Repository envelope decoder. |
|
||
| Sites per profile | 1,000 | Repository/query pagination. |
|
||
| Records per aggregate kind/site | 100,000 | Decode/command validation. |
|
||
| One sync operation plaintext | 256 KiB | Journal command codec. |
|
||
| Sync wire event | <= 48 KiB after encoding | Shared protocol before send. |
|
||
| Snapshot plaintext | 32 MiB | Streaming snapshot builder/reader. |
|
||
| Snapshot chunks | 1,024; unique indexes only | Reassembly before allocation. |
|
||
| Compression expansion | <= 20:1 and <=32 MiB | Streaming decompressor. |
|
||
| Concurrent network requests | 32 process-wide; 1 sync stream/site | Request manager. |
|
||
| Pending persistence | 1 coalesced revision/site + bounded journal | Repository writer. |
|
||
| Image encoded bytes | 20 MiB | Ingestion stream before decode. |
|
||
| Image dimensions/pixels | <= 8,192 each; <= 40 MP | Decoder header/limits before allocation. |
|
||
| Animation frames | <= 120 and <= 30 seconds | Manifest/request preflight. |
|
||
| Total decoded animation pixels | <= 30M pixels (~90 MiB RGB) | Shared media budget. |
|
||
| GIF output | 50 MiB | Counting writer. |
|
||
| Video output | 200 MiB and below format's checked size ceiling | Counting writer/container preflight. |
|
||
| Report text | 1 MiB/report; 64 KiB/field | Domain constructors. |
|
||
| Meeting transcript | 5 MiB | Ingest/editor/AI boundary. |
|
||
| PDF/DOCX | 500 pages and 100 MiB output | Paginated counting writer. |
|
||
| AI request/response | 64 KiB / 16 KiB; 30 s; 1 active/site | AI coordinator. |
|
||
| Undo/offline journal | 10,000 ops or 64 MiB/site before compaction | Sync/repository. |
|
||
| UI event | p95 < 8 ms desktop / 12 ms mobile | No disk/network/decode/export in handler. |
|
||
| Site switch | p95 < 100 ms for metadata; content loads incrementally | Runtime benchmark. |
|
||
|
||
All count/size/rate arithmetic is checked. Limit errors preserve the prior durable revision and delete incomplete temporary output.
|
||
|
||
---
|
||
|
||
## 8. Dependency-ordered implementation tranches
|
||
|
||
Each tranche is a separately tested commit and push. Security containment must not wait for broad refactoring.
|
||
|
||
### SITE-00 — Site-owned truthful CI
|
||
|
||
**Priority:** P0
|
||
**Effort:** 2–3 person-days
|
||
**Depends on:** none; may proceed in parallel with SITE-01
|
||
|
||
**Change**
|
||
|
||
- Add `.forgejo/workflows/nigig-site.yml` covering Site-owned files, shared protocol/server, Cargo manifests/lock, and relevant dependencies.
|
||
- Use one build job and disabled test/dev debug info on memory-limited runners.
|
||
- Split check, unit, integration, runtime UI, sync interoperability, media limits, migration, clippy, and security/supply-chain jobs.
|
||
- Make missing server/config an explicit skipped job status in development and a hard failure in release CI.
|
||
- Preserve full logs and peak RSS/timeout artifacts.
|
||
|
||
**Tests / exit**
|
||
|
||
- Obtain a definitive test result; signal 9 is reported as infrastructure/resource failure.
|
||
- A test that returns before assertions cannot count as pass.
|
||
- Every path filter and source scan proves it matched owned files.
|
||
- Cargo/clippy failures are never hidden by shell filtering or `|| true`.
|
||
|
||
**Rollback:** runner tuning may change; mandatory ownership and honest status remain.
|
||
|
||
### SITE-01 — Emergency containment without destroying data
|
||
|
||
**Priority:** P0 immediate
|
||
**Effort:** 1–3 person-days
|
||
**Depends on:** none
|
||
|
||
**Change**
|
||
|
||
- Remove/disable Push/Pull and all automatic current sync code in production builds.
|
||
- On any non-successful store open, enter `RecoveryRequired` read-only UI; do not call `SiteStore::default`, seed Muthaiga Villas, save, or mutate the original.
|
||
- Disable confidential writes when encryption/keyring is unavailable; show explicit safe-mode state.
|
||
- Disable GIF/video generation, remote AI, and path-based export/OCR entry points until their limit/privacy tranches land; manual text/photo capture may continue only through safe asset ingestion.
|
||
- Preserve original files and display recovery/export-support actions without decrypting/logging content.
|
||
|
||
**Tests / exit**
|
||
|
||
- Missing key, wrong key, tamper, malformed JSON, future version, permission error, and empty file cannot create/overwrite a store.
|
||
- Default production build has no reachable sync send/pull path.
|
||
- A feature flag/config cannot bypass containment accidentally.
|
||
- Existing valid encrypted data remains readable and byte-identical until explicit mutation.
|
||
|
||
**Rollback:** unsafe capabilities stay disabled. There is no acceptable rollback to plaintext sync or destructive seeding.
|
||
|
||
### SITE-02 — Fail-closed recoverable encrypted repository
|
||
|
||
**Priority:** P0
|
||
**Effort:** 8–12 person-days
|
||
**Depends on:** SITE-01
|
||
|
||
**Change**
|
||
|
||
- Replace `load_or_create_dek` with distinct setup and existing-store open flows. An existing envelope names `store_id` and `key_id`; missing key is `KeyUnavailable`, never “create another key.”
|
||
- Make encryption/decryption/sealing return typed `Result`; eliminate plaintext fallback.
|
||
- Bind envelope header/version/store/aggregate/key ID to AEAD associated data and validate nonce/key-length/version exactly.
|
||
- Add user-controlled recovery/key escrow design, rotation, and secure deletion after security review; do not invent password crypto ad hoc.
|
||
- Replace `save` with an injected `SiteRepository` returning durable revision results. Use unique temp names, restrictive permissions at creation, checked write/flush/sync/rename/directory sync, and cleanup.
|
||
- Replace unbounded full-store channel with bounded coalescing per-site writers and explicit `flush_and_shutdown`.
|
||
- Expose persistence health and last durable revision to UI.
|
||
|
||
**Tests / exit**
|
||
|
||
- Fault injection at key lookup, random generation, serialization, encryption, create, chmod, write, flush, sync, rename, and shutdown.
|
||
- Wrong/missing/rotated key and modified header/ciphertext are distinct recoverable errors.
|
||
- No plaintext bytes/known PII appear in data, temp, backup, or journal files.
|
||
- Queue depth is bounded; final accepted mutation either flushes on graceful shutdown or remains explicitly unsaved.
|
||
- Independent crypto/security review approves primitive use, nonce policy, associated data, key lifecycle, and recovery design.
|
||
|
||
**Migration:** legacy plaintext is read only after explicit user consent and key setup, then encrypted to a new location and verified before original disposition. Existing encrypted `NIGIG1` input is preserved and migrated without invoking key creation on lookup failure.
|
||
|
||
**Rollback:** use the prior encrypted reader against preserved originals; never emit plaintext or reseed.
|
||
|
||
### SITE-03 — Site-scoped versioned aggregates and explicit context
|
||
|
||
**Priority:** P0
|
||
**Effort:** 7–11 person-days
|
||
**Depends on:** SITE-02
|
||
|
||
**Change**
|
||
|
||
- Split monolithic `SiteStore` into typed versioned aggregates per site plus device-local preferences.
|
||
- Add opaque `SiteId`, `ReportId`, `TaskId`, `WorkerId`, `ScanId`, `SupplierId`, `MeetingId`, `AssetId`, `ReminderId`, `UserId`, and `DeviceId` newtypes.
|
||
- Introduce `SiteContext`; remove `selected_or_first` from command/query authorization.
|
||
- Add repository query pagination/indexes and aggregate revision compare-and-swap.
|
||
- Move selected site, transient screen state, and chat presentation cache out of replicated domain payloads.
|
||
|
||
**Tests / exit**
|
||
|
||
- APIs cannot compile a site mutation/query/export without `SiteContext`.
|
||
- A/B sentinel tests cover every aggregate, cache, asset, export, reminder, and job.
|
||
- Missing/stale selection is `NoSiteSelected`, never substitution with first site.
|
||
- A one-site mutation does not clone/serialize unrelated sites or PII.
|
||
- Duplicate/wrong-type/cross-site IDs are rejected.
|
||
|
||
**Migration:** split legacy whole store by `site_id`; global suppliers/ambiguous records require explicit assignment or quarantined shared-directory policy. Keep mapping and original encrypted bytes.
|
||
|
||
**Rollback:** compatibility reader may expose old data read-only; new writes never return to a global blob.
|
||
|
||
### SITE-04 — Domain command service, validation, and immutable audit
|
||
|
||
**Priority:** P0/P1
|
||
**Effort:** 8–12 person-days
|
||
**Depends on:** SITE-03
|
||
|
||
**Change**
|
||
|
||
- Make domain fields private where mutation needs invariants.
|
||
- Add `DomainCommandService` with expected revision, actor/device, site context, reason, timestamp source, validation, atomic aggregate update, and audit event.
|
||
- Validate uniqueness, parent/reference ownership, finite/range values, latitude/longitude, date order, duration, one-report-per-site/day, scan deduplication, and record/text limits.
|
||
- Use fixed-decimal quantity plus typed unit where commercial/measured quantities matter.
|
||
- Persist append-only audit events with hash/integrity linkage appropriate to the repository threat model; audit is not user-editable history.
|
||
|
||
**Tests / exit**
|
||
|
||
- Every public mutation has success, unauthorized, invalid transition/reference/value, stale revision, and rollback tests.
|
||
- NaN/inf, negative quantities, impossible coordinates/dates, duplicate IDs/scans/reports, cross-site parents/suppliers/assets fail.
|
||
- Failed command leaves aggregate, revision, audit, and sync journal unchanged.
|
||
- Audit identifies actor/device/command/target/prior-new revision/reason without logging secret payloads.
|
||
|
||
**Rollback:** aggregates remain read-only if command migration fails; no public-field bypass.
|
||
|
||
### SITE-05 — Authentication, site roles, and PII policy
|
||
|
||
**Priority:** P0 before sync
|
||
**Effort:** 8–14 person-days plus privacy/security review
|
||
**Depends on:** SITE-03, SITE-04
|
||
|
||
**Change**
|
||
|
||
- Define authenticated user/device session and site membership/roles/capabilities. Server and client enforce the same policy at different trust boundaries.
|
||
- Map commands to capabilities: report author/submit/approve, worker PII read/write/export, task inspect, procurement edit, meeting manage, site admin, key/device management.
|
||
- Separate stable worker/person record from attendance scan; daily tables reference `WorkerId` rather than duplicating full ID/contact data.
|
||
- Define purpose, masking, search, export, retention, correction, deletion/anonymization, backup expiration, and ID-image handling.
|
||
- Redact PII/secrets from logs, errors, crash reports, analytics, filenames, and notification bodies.
|
||
|
||
**Tests / exit**
|
||
|
||
- Deny-by-default role matrix at command, query, export, sync, and server endpoints.
|
||
- Removed member/device loses future key access and server authorization according to documented revocation limits.
|
||
- Worker ID is masked by default and omitted from broad report/export roles.
|
||
- Retention/deletion/anonymization propagates to indexes, assets, sync operations, and backups under stated policy.
|
||
- Privacy/security review and Kenya/target-market legal review approve processing flows.
|
||
|
||
**Rollback:** disable remote/PII workflows or make them local read-only; never rely on screen hiding as authorization.
|
||
|
||
### SITE-06 — Enforced report/task/inspection/procurement/meeting workflows
|
||
|
||
**Priority:** P1
|
||
**Effort:** 10–16 person-days
|
||
**Depends on:** SITE-04, SITE-05
|
||
|
||
**Change**
|
||
|
||
- Define legal state machines and commands for site, report, construction task, inspection, procurement line, meeting, and minutes.
|
||
- Require submitter/approver separation if policy requires; approval/rejection reason and immutable approved revision.
|
||
- Validate task hierarchy, dates, quantities/units, inspections/assignees/outcomes, supplier references, meeting attendees/duration/status, and minutes approval.
|
||
- Replace byte-truncated “auto-summary” with no summary until real summarization is accepted.
|
||
- Define reopen/amendment behavior instead of mutating approved history.
|
||
|
||
**Tests / exit**
|
||
|
||
- Transition tables test every allowed/forbidden edge and role.
|
||
- Approved reports/minutes are immutable; amendment produces linked revision and audit.
|
||
- Parent/site, supplier/site, inspection/task, minutes/meeting, and attendee/user references validate.
|
||
- Unicode transcript of any valid text never panics.
|
||
- Templates are explicit user actions and visibly identified, not recovery seeds.
|
||
|
||
**Rollback:** block transitions and preserve drafts; do not permit arbitrary status mutation.
|
||
|
||
### SITE-07 — Managed asset ingestion and lifecycle
|
||
|
||
**Priority:** P0 media prerequisite
|
||
**Effort:** 7–11 person-days
|
||
**Depends on:** SITE-02, SITE-03, SITE-05
|
||
|
||
**Change**
|
||
|
||
- Replace stored filesystem strings with site-scoped `AssetId` plus media metadata, content digest, ownership, source, creation, retention, and encryption key epoch.
|
||
- Ingest picker/camera handles by streaming into a quarantine temp file under quota; sniff/decode declared format, check encoded bytes and header dimensions before full allocation, then atomically publish.
|
||
- Use decoder allocation limits and checked pixel/channel arithmetic; reject decompression bombs and unsupported metadata.
|
||
- Generate bounded thumbnails separately. Strip EXIF/location by default; attach GPS only after explicit product/user policy.
|
||
- Prevent symlink/path races by never reopening an arbitrary caller path as authority.
|
||
- Implement reference counting or explicit ownership for deletion/retention.
|
||
|
||
**Tests / exit**
|
||
|
||
- Truncated, polyglot, extension-spoofed, huge-dimension, high-compression, symlink-swap, duplicate-content, quota, disk-full, and cancellation corpus.
|
||
- Domain/export/OCR/media APIs accept `AssetId`, not raw path strings.
|
||
- Delete/retention removes all derived thumbnails/transcodes and respects approved-record retention rules.
|
||
- Assets from Site A are inaccessible under Site B context even with guessed IDs.
|
||
|
||
**Rollback:** retain original external path only in migration quarantine/read-only UI; new captures use managed assets.
|
||
|
||
### SITE-08 — Shared sync protocol and threat-model ADR
|
||
|
||
**Priority:** P0
|
||
**Effort:** 6–10 person-days
|
||
**Depends on:** SITE-01, SITE-03, SITE-05
|
||
|
||
**Change**
|
||
|
||
- Write an ADR/protocol specification with threat model, metadata leakage, identity/membership, key epochs, event fields, size limits, replay/order, snapshot/compaction, conflict, deletion, revocation, recovery, and version negotiation.
|
||
- Create one workspace protocol crate used by app, server, and tests; no hand-written parallel JSON shims.
|
||
- Define dedicated opaque sync endpoints/event types; do not piggyback application snapshots as ordinary chat text.
|
||
- Define canonical byte encoding and test vectors across implementations/versions.
|
||
- Select an established audited group encryption/key-management protocol or maintained implementation (for example MLS where platform support is viable). Any simpler design requires independent cryptographic review before code.
|
||
|
||
**Tests / exit**
|
||
|
||
- Protocol crate rejects unknown critical versions/fields, wrong site, oversized lengths/counts, duplicate indexes, invalid canonical encoding, and unsupported algorithm suites before large allocation.
|
||
- App/server compile against the same request/response types and HTTP methods.
|
||
- Threat model and crypto choice receive independent security review.
|
||
- Version negotiation cannot silently downgrade confidentiality/integrity.
|
||
|
||
**Rollback:** sync remains disabled; no “temporary” plaintext protocol ships.
|
||
|
||
### SITE-09 — Authenticated site-scoped E2EE and device/key lifecycle
|
||
|
||
**Priority:** P0
|
||
**Effort:** 12–20 person-days plus external review
|
||
**Depends on:** SITE-05, SITE-08
|
||
|
||
**Change**
|
||
|
||
- Provision per-site group context/key epochs through the selected reviewed protocol; bind authenticated user devices to site membership.
|
||
- Encrypt application payload client-side; include only minimum routing/version metadata outside ciphertext.
|
||
- Authenticate sender/device and bind site ID, protocol version, key epoch, operation ID, sequence, and payload digest as protected content/associated data.
|
||
- Implement device add/verify/remove, member add/remove, epoch rotation, lost-device recovery, backup/recovery, and key zeroization according to chosen library guarantees.
|
||
- Pin production TLS/security policy in addition to E2EE; allow loopback HTTP only in explicit development builds.
|
||
|
||
**Tests / exit**
|
||
|
||
- Known-answer vectors plus wrong site/key/epoch/device, modified header/body, nonce reuse guard, replay, reordering, duplicate, removed device, downgrade, and lost-key recovery tests.
|
||
- Server/database/log capture contains no worker/report/application plaintext or client group keys.
|
||
- A member without Site A membership cannot decrypt/inject A even if sharing another chat room.
|
||
- External security assessment has no unresolved critical/high findings.
|
||
|
||
**Rollback:** disable sync and retain encrypted local journal; never downgrade to TLS-only/plain room messages.
|
||
|
||
### SITE-10 — Operation journal, tombstones, and deterministic convergence
|
||
|
||
**Priority:** P0
|
||
**Effort:** 10–16 person-days
|
||
**Depends on:** SITE-04, SITE-08, SITE-09
|
||
|
||
**Change**
|
||
|
||
- Replicate validated domain commands/operations for one site, not whole mutable stores.
|
||
- Give operations globally unique ID, device sequence, base/entity revision, actor, schema, and cryptographically authenticated ordering metadata.
|
||
- Add tombstones with retention/compaction rules; edits/deletes must propagate.
|
||
- Define per-aggregate conflict rules. Use server cursor/log order, revision preconditions, CRDTs where justified, or explicit user conflict; do not let arbitrary future wall clocks win.
|
||
- Exclude chat cache, selection, secrets, keys, temporary files, and sync envelopes from snapshots.
|
||
- Build streaming encrypted snapshots for bootstrap/compaction with hash tree/digest, byte/record limits, and acknowledgement before journal truncation.
|
||
|
||
**Tests / exit**
|
||
|
||
- Multi-replica property tests randomly reorder/duplicate/drop/retry operations and prove convergence after eventual delivery.
|
||
- Edit/edit, edit/delete, delete/recreate, stale base, offline long fork, future/old clock, device reset, and compaction boundary cases.
|
||
- Syncing once does not make the next snapshot grow recursively.
|
||
- A malicious operation cannot bypass domain authorization/invariants merely because it decrypts.
|
||
- Journal/snapshot stays within §7 and interruption never discards unacknowledged ops.
|
||
|
||
**Rollback:** retain local encrypted journal and require manual export; do not apply ambiguous remote state.
|
||
|
||
### SITE-11 — Correlated bounded network client
|
||
|
||
**Priority:** P0
|
||
**Effort:** 5–8 person-days
|
||
**Depends on:** SITE-08
|
||
|
||
**Change**
|
||
|
||
- Replace static request IDs/singleton pending slots with `RequestManager` keyed by unique operation ID and carrying endpoint, site, room if applicable, base revision, deadline, retry/idempotency, and cancellation.
|
||
- Bound concurrent/pending requests and response body bytes; validate status/content type/typed body and required nonempty IDs.
|
||
- Use cursor-based pagination; do not fetch an entire room history to discover snapshots.
|
||
- Redact tokens and bodies from errors/logs; credentials use secure storage and explicit session lifecycle.
|
||
- Deliver response only to matching live controller/context.
|
||
|
||
**Tests / exit**
|
||
|
||
- 100 concurrent out-of-order create/send/fetch/sync operations correlate exactly.
|
||
- Timeout/cancel/site switch/retry/duplicate response/oversized body/invalid JSON/empty success ID tests.
|
||
- Idempotent retry cannot create duplicate room/events.
|
||
- Queue saturation is an explicit error/backpressure state.
|
||
|
||
**Rollback:** network features remain disabled; no global pending-slot fallback.
|
||
|
||
### SITE-12 — Reproducible real server interoperability
|
||
|
||
**Priority:** P0 release prerequisite for sync
|
||
**Effort:** 8–15 person-days depending on server import
|
||
**Depends on:** SITE-08 through SITE-11
|
||
|
||
**Change**
|
||
|
||
- Vendor/import the actual supported `nimanyatta` server/protocol history or depend on a reproducibly pinned source available to CI.
|
||
- Add migrations/test server launcher and typed endpoints for auth, membership, opaque events, cursors, snapshots, limits, rate limiting, and idempotency.
|
||
- Replace `sync_e2e` early-return behavior with explicit ignored developer test plus mandatory CI integration job that launches the pinned server.
|
||
- Test two users, three devices, two sites, removal/revocation, offline edits, and server restart/database migration.
|
||
|
||
**Tests / exit**
|
||
|
||
- Fresh checkout can build and launch app protocol tests with no sibling repository.
|
||
- App and server use identical methods/routes/types; protocol drift is a compile/test failure.
|
||
- Database inspection verifies ciphertext-only application payload.
|
||
- Authz, rate/size limits, cursor semantics, idempotency, revocation, and schema migration pass.
|
||
- A required server job cannot be classified as pass when not executed.
|
||
|
||
**Rollback:** deploy no sync client UI; local encrypted operation journal remains intact.
|
||
|
||
### SITE-13 — Private, correct, correlated AI refinement
|
||
|
||
**Priority:** P0/P1
|
||
**Effort:** 5–8 person-days
|
||
**Depends on:** SITE-04, SITE-05, SITE-11
|
||
|
||
**Change**
|
||
|
||
- Instantiate the chosen provider with matching `ProviderKind`, model, endpoint, and credential source.
|
||
- Add operation/site/report/field/base revision IDs to request and result.
|
||
- Enforce request/response/time budgets; timeout/cancel/error/function call/partial stream is failure.
|
||
- Require explicit consent and show provider/data fields/region-retention link before remote processing. Minimize/redact input.
|
||
- Stream into preview only; apply as a domain command after complete response validation and explicit acceptance.
|
||
- Store credentials in platform secret storage; never mutate process environment as product settings.
|
||
|
||
**Tests / exit**
|
||
|
||
- Fake providers prove selection, data minimization, complete success, partial timeout, stale result, cancellation, oversized output, malformed/function response, and site switch.
|
||
- Original report is unchanged on all failures and before acceptance.
|
||
- Role/privacy policy gates who may submit data and persist refined text.
|
||
- Logs/artifacts contain no prompt, key, ID number, or report body.
|
||
|
||
**Rollback:** AI stays disabled; manual text remains fully functional.
|
||
|
||
### SITE-14 — Real OCR or truthful capture-only flow
|
||
|
||
**Priority:** P1 privacy
|
||
**Effort:** 6–12 person-days plus platform/license review
|
||
**Depends on:** SITE-05, SITE-07
|
||
|
||
**Change**
|
||
|
||
- Rename current behavior to crop/enhance if no OCR engine is shipped.
|
||
- If OCR is enabled, integrate a maintained on-device engine with pinned model/version/license, language coverage, confidence/field extraction, and no network by default.
|
||
- Process managed `AssetId` under media budgets in a bounded worker; correlate result to site/worker/base revision.
|
||
- Present suggestions with confidence for user confirmation; never auto-approve identity.
|
||
- Define ID-image deletion immediately after verified extraction unless retention has explicit lawful purpose/consent.
|
||
|
||
**Tests / exit**
|
||
|
||
- Representative permitted ID fixtures measure field precision/recall and false-positive behavior; synthetic fixtures alone are insufficient.
|
||
- Rotated, blurred, glare, Unicode names, no text, adversarial image, cancel, and stale/site-switch tests.
|
||
- UI never says OCR found data when suggestions are `None`.
|
||
- Privacy/license/security review passes and no image leaves device absent explicit remote-OCR consent.
|
||
|
||
**Rollback:** capture/manual entry only, with no OCR claim.
|
||
|
||
### SITE-15 — Streaming bounded GIF/video processing
|
||
|
||
**Priority:** P0 media
|
||
**Effort:** 8–12 person-days
|
||
**Depends on:** SITE-07
|
||
|
||
**Change**
|
||
|
||
- Accept managed assets and a `MediaBudget`, preflight encoded bytes/dimensions/frame count/duration/fps/output/container limits before decoding.
|
||
- Decode/resize/encode one bounded frame at a time; do not retain source images, resized images, RGB copies, JPEGs, `movi`, and final output simultaneously.
|
||
- Use checked arithmetic and fallible reservations/conversions for every pixel/RIFF/GIF/JPEG count.
|
||
- Prefer a maintained encoder/container library. If AVI remains, cap below 32-bit RIFF limits, include/validate needed indexing, and independently test playback.
|
||
- Use collision-resistant asset IDs, unique temp output, atomic publish, cancellation cleanup, and quotas.
|
||
|
||
**Tests / exit**
|
||
|
||
- Huge headers, decompression bombs, 0/too-high fps, >120 frames, mismatched dimensions, maximum counts, JPEG/output expansion, integer boundaries, disk full, cancel, and duplicate simultaneous jobs.
|
||
- Peak RSS stays under the declared decoded/output budget; test records metrics.
|
||
- Independent GIF/video decoders parse and play every golden output with expected frame count/duration/dimensions.
|
||
- No arbitrary path open and no second-resolution filename remains.
|
||
|
||
**Rollback:** disable animation/video; preserve original managed photos.
|
||
|
||
### SITE-16 — Portable panic-free PDF/DOCX exports
|
||
|
||
**Priority:** P1
|
||
**Effort:** 8–13 person-days
|
||
**Depends on:** SITE-04 through SITE-07, SITE-11 for job correlation
|
||
|
||
**Change**
|
||
|
||
- Return `Result<ExportArtifact, ExportError>` throughout; remove production `unwrap` and partial success.
|
||
- Iterate/truncate/wrap by Unicode grapheme/glyph boundaries, not byte offsets; embed a reviewed Unicode font with fallback.
|
||
- Resolve authorized `AssetId`s and embed compressed photos/thumbnails with captions/location policy. Never print local paths.
|
||
- Build valid DOCX image parts, relationships, content types, dimensions, and alt text using a maintained OOXML layer where feasible.
|
||
- Stream/paginate under page/byte/image budgets in a bounded export coordinator and deliver only after final picker/write completion.
|
||
- Apply role/PII minimization and watermark/status policy to compiled multi-site reports.
|
||
|
||
**Tests / exit**
|
||
|
||
- Emoji, combining marks, CJK/Arabic/long unbroken text, 500-page boundary, missing/deleted/unauthorized/corrupt photos, write failure, and cancellation never panic.
|
||
- Independent PDF parser/render and LibreOffice/Word-compatible DOCX open validate pages, text, images, relationships, and no repair warnings.
|
||
- Extracted package/PDF contains image bytes and no source filesystem paths.
|
||
- Export from Site A cannot include Site B without an explicit authorized multi-site scope.
|
||
|
||
**Rollback:** export text-only with explicit omitted-image warnings or disable the format; never claim photo inclusion.
|
||
|
||
### SITE-17 — Durable site-time-zone reminder state machine
|
||
|
||
**Priority:** P1
|
||
**Effort:** 5–8 person-days
|
||
**Depends on:** SITE-03 through SITE-05
|
||
|
||
**Change**
|
||
|
||
- Store IANA site time zone and validate local working-hour/calendar inputs; use device zone only by explicit policy.
|
||
- Make fire-time calculation return `Result`, handle DST ambiguous/nonexistent times according to documented policy, and remove panic/fallback-to-UTC guessing.
|
||
- Persist `PendingRegistration` with idempotency key before OS call; then persist `Registered`/`RegistrationFailed` and platform handle.
|
||
- Implement update/cancel/reschedule/reconcile on startup and permissions/time-zone/clock changes.
|
||
- Mark `Delivered` only on confirmed/successful post semantics available from platform; otherwise record attempted/unknown/failed honestly.
|
||
- Deduplicate app/OS paths by stable tag and state.
|
||
|
||
**Tests / exit**
|
||
|
||
- Nairobi and DST zones, midnight/year/month boundaries, invalid hour, past/future, clock rollback/forward, restart between each state, denied permission, OS failure, duplicate tick, cancel/reschedule.
|
||
- Site A zone never drives Site B.
|
||
- Repository and fake OS scheduler fault injection prove recoverable reconciliation.
|
||
- Notification body follows PII minimization policy.
|
||
|
||
**Rollback:** in-app due list only; no unreliable OS scheduling claims.
|
||
|
||
### SITE-18 — Screen/controller isolation, responsive UX, and observability
|
||
|
||
**Priority:** P1/P2
|
||
**Effort:** 10–16 person-days
|
||
**Depends on:** SITE-03 through enabled feature tranches
|
||
|
||
**Change**
|
||
|
||
- Replace direct `SiteStore` reads/mutations in screens with controllers and immutable revisioned view models.
|
||
- Route every screen with explicit `SiteContext`; invalidate/cancel old cache/jobs on switch.
|
||
- Remove static singleton action payloads and dead/inert actions.
|
||
- Virtualize large reports/workers/tasks/procurement/meeting lists; do not clone whole profile in draw.
|
||
- Add accessible labels/focus/touch targets/error/recovery/offline/conflict states.
|
||
- Instrument persistence revisions/latency/queue, sync encrypted bytes/cursor/retries/conflicts, media bytes/pixels/frames/RSS, AI duration, export pages/bytes, and frame/event latency. Never record content/PII.
|
||
|
||
**Tests / exit**
|
||
|
||
- Automated A/B route/switch suite checks every screen, dialog, async completion, cache, and export.
|
||
- Draw/event instrumentation detects disk/network/decode/export work and fails.
|
||
- 100k-record virtualized fixtures and site switch meet §7 budgets.
|
||
- Metrics cardinality/content review proves no IDs, names, paths, prompt/report text, tokens, or coordinates leak.
|
||
- Desktop/mobile lifecycle and accessibility tests exercise actual actions after app startup.
|
||
|
||
**Rollback:** presentation pieces may roll back; explicit site/controller/domain boundaries may not.
|
||
|
||
### SITE-19 — Migration, adversarial, interoperability, and release evidence
|
||
|
||
**Priority:** P0 release gate
|
||
**Effort:** 8–14 person-days plus external/device testing
|
||
**Depends on:** every enabled-feature tranche
|
||
|
||
**Change**
|
||
|
||
- Build sanitized legacy corpus: plaintext/raw JSON, plaintext envelope, valid encrypted, missing key, wrong key, tampered/truncated/future, multi-site/PII/media paths, recursive sync messages.
|
||
- Add storage fault, domain property, authorization, protocol fuzz, multi-replica convergence, media bomb, Unicode export, AI stale/cancel, reminder, and real runtime journeys.
|
||
- Run multi-device/server restart/migration/revocation tests and long offline/online soak.
|
||
- Perform independent security/privacy/crypto review and remediate critical/high findings.
|
||
- Publish capability matrix and release evidence with commit, toolchain, server schema, platform/device, limits, and known limitations.
|
||
|
||
**Tests / exit**
|
||
|
||
- Migration operates on copies, verifies encrypted outputs, and demonstrates rollback without data invention/loss.
|
||
- Zero unexplained/expired ignored tests; required server/device jobs actually execute.
|
||
- No PII plaintext appears in local encrypted artifacts, wire/server storage, logs, or CI artifacts.
|
||
- A/B isolation, role matrix, convergence, replay/forgery, resource limits, and forced failures all pass.
|
||
- Peak memory/latency/output meet §7 on supported devices.
|
||
|
||
**Rollback:** release is blocked or the failing capability remains disabled through SITE-01.
|
||
|
||
---
|
||
|
||
## 9. Detailed legacy migration and recovery policy
|
||
|
||
1. **Never open destructively.** Read bounded original bytes once, hash them, and work on a copy.
|
||
2. **Classify before action:** absent, zero length, raw legacy JSON, explicit plaintext envelope, encrypted known version/key ID, encrypted missing key, authentication failure, corrupt payload, or unsupported future version.
|
||
3. **Do not create a key while opening encrypted data.** Setup creates keys only for a confirmed new profile/store ID.
|
||
4. **Require encryption before migration writes.** If key setup fails, retain read-only recovery state; do not write plaintext.
|
||
5. **Split by site.** Reports/workers/tasks/procurement/meetings/directories/assets map by validated site ID. Dangling and global records are quarantined for explicit resolution.
|
||
6. **Do not replicate device state.** Selected site and cached chat presentation are excluded.
|
||
7. **Import assets.** Canonicalize only for one-time migration, enforce byte/pixel limits, copy into managed asset storage, and replace path with `AssetId`. Missing/unsafe files become warnings, not arbitrary reads.
|
||
8. **Preserve PII policy.** ID images are not imported automatically without a retention decision.
|
||
9. **Write new aggregates atomically, reopen/decrypt/validate, compare record counts/digests, then activate new manifest.**
|
||
10. **Keep originals encrypted/restricted for a stated recovery window.** Deletion requires explicit confirmation and backup policy.
|
||
11. **Never seed demo data as migration/recovery.** A new empty profile is a valid explicit user choice.
|
||
12. **No automatic downgrade.** Newer-schema data stays read-only for an old binary.
|
||
|
||
---
|
||
|
||
## 10. Required CI command matrix
|
||
|
||
Memory-constrained baseline commands:
|
||
|
||
```bash
|
||
CARGO_BUILD_JOBS=1 CARGO_PROFILE_DEV_DEBUG=0 cargo check --locked -p nigig-site --all-targets
|
||
CARGO_BUILD_JOBS=1 CARGO_PROFILE_TEST_DEBUG=0 cargo test --locked -p nigig-site --lib -- --test-threads=1
|
||
CARGO_BUILD_JOBS=1 CARGO_PROFILE_TEST_DEBUG=0 cargo test --locked -p nigig-site --tests -- --test-threads=1
|
||
cargo fmt -p nigig-site -- --check
|
||
CARGO_BUILD_JOBS=1 CARGO_PROFILE_DEV_DEBUG=0 cargo clippy --locked -p nigig-site --all-targets -- -D warnings
|
||
git diff --check
|
||
```
|
||
|
||
Required named jobs as tranches land:
|
||
|
||
```bash
|
||
cargo test --locked -p nigig-site --test storage_recovery -- --test-threads=1
|
||
cargo test --locked -p nigig-site --test project_site_isolation -- --test-threads=1
|
||
cargo test --locked -p nigig-site --test authorization_matrix -- --test-threads=1
|
||
cargo test --locked -p nigig-site --test protocol_adversarial -- --test-threads=1
|
||
cargo test --locked -p nigig-site --test sync_convergence -- --test-threads=1
|
||
cargo test --locked -p nigig-site --test media_limits -- --test-threads=1
|
||
cargo test --locked -p nigig-site --test export_interop -- --test-threads=1
|
||
cargo test --locked -p nigig-site --test runtime_ui -- --test-threads=1
|
||
cargo test --locked -p nigig-site --test sync_e2e_required -- --test-threads=1
|
||
```
|
||
|
||
Release CI launches the pinned server itself; `NIMANYATTA_E2E_URL` cannot be an optional condition for the required job. Fuzzers and independent parsers run with fixed wall-clock/memory ceilings and retain crashing inputs.
|
||
|
||
---
|
||
|
||
## 11. Release gates
|
||
|
||
### Local/offline release
|
||
|
||
- [ ] Current sync is absent from production build.
|
||
- [ ] Missing/wrong key, tamper, corruption, future version, empty, and I/O failures are distinct non-destructive states.
|
||
- [ ] No plaintext fallback exists; durable save errors are surfaced.
|
||
- [ ] Repository queues/writes are bounded, atomic, and flushed/reconciled on lifecycle exit.
|
||
- [ ] Every command/query/export/job uses explicit typed site context.
|
||
- [ ] Domain transitions, references, finite/range rules, roles, revisions, and audit are centrally enforced.
|
||
- [ ] Worker PII and ID-image lifecycle policy is approved and tested.
|
||
- [ ] All enabled media uses managed assets and passes encoded/pixel/frame/output/peak-memory limits.
|
||
- [ ] OCR is real and measured or labelled capture-only.
|
||
- [ ] PDF/DOCX are Unicode-safe, embed authorized photos, return errors, and pass independent consumers.
|
||
- [ ] AI is disabled or passes provider/privacy/correlation/acceptance gates.
|
||
- [ ] Reminders pass time-zone/durability/idempotency/reconciliation gates or remain in-app only.
|
||
- [ ] Site-owned CI has a definitive green result with real runtime assertions.
|
||
|
||
### Additional sync release gates
|
||
|
||
- [ ] Shared protocol and threat-model ADR are approved.
|
||
- [ ] App/server/tests use one protocol crate and reproducible in-repo/pinned server.
|
||
- [ ] Authenticated site-scoped E2EE passes external review with no open critical/high findings.
|
||
- [ ] Device/member lifecycle, revocation, recovery, replay, downgrade, cross-site, and ciphertext-at-server tests pass.
|
||
- [ ] Operation journal/tombstones/conflicts converge under randomized multi-replica tests.
|
||
- [ ] Network client correlates bounded concurrent requests and validates all responses.
|
||
- [ ] Required E2E server job actually executes; no silent return/skip counts as pass.
|
||
- [ ] Product copy accurately states metadata/endpoint/availability limitations.
|
||
|
||
---
|
||
|
||
## 12. Delivery, push safety, and rollback protocol
|
||
|
||
For each `SITE-NN` tranche:
|
||
|
||
1. Record starting SHA and keep a clean worktree.
|
||
2. Add regression/adversarial tests that fail against the parent commit.
|
||
3. Run the tranche matrix under memory/resource limits plus `git diff --check`.
|
||
4. Commit only that independently reviewable change using its tranche ID.
|
||
5. Immediately before push, run `git fetch origin main` and compare remote/merge base.
|
||
6. If remote moved, inspect all incoming changes and rebase/merge without discarding them; rerun tests after resolution.
|
||
7. Push without force and verify the exact commit exists remotely before starting the next pushed tranche.
|
||
8. If authentication is unavailable, report the exact blocker and preserve the tested local commit. Never claim success, expose credentials, or overwrite remote work later.
|
||
|
||
Rollback defaults to **feature off + preserved encrypted data**. Never roll back to plaintext storage/sync, first-site fallback, destructive demo seeding, unrestricted path decode, public workflow mutation, or stale async acceptance.
|
||
|
||
---
|
||
|
||
## 13. Critical path and cross-plan dependencies
|
||
|
||
```text
|
||
SITE-01 containment → SITE-02 encrypted repository → SITE-03 site aggregates/context
|
||
├→ SITE-04 commands/audit → SITE-05 auth/privacy → SITE-06 workflows
|
||
└→ SITE-07 managed assets → SITE-14/15/16
|
||
SITE-03 + SITE-05 → SITE-08 protocol → SITE-09 E2EE → SITE-10 convergence
|
||
└────────→ SITE-11 client → SITE-12 real server
|
||
SITE-04/05/11 → SITE-13 AI
|
||
SITE-03/04/05 → SITE-17 reminders
|
||
All enabled paths + SITE-00 → SITE-18 → SITE-19
|
||
```
|
||
|
||
- Build/Traffic/CAD work does not justify delaying Site's emergency containment.
|
||
- Future integration with `nigig-build` requires an authenticated mapping between Build project ID and Site ID; never sync one app's global store into the other.
|
||
- Shared chat rooms may coexist with sync, but sync payloads must use dedicated opaque protocol events and keys, not visible text bodies.
|
||
- A safe release may omit sync, AI, OCR, video, DOCX, or OS reminders. It may not ship unsafe substitutes for them.
|