You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Five same-session refinements folded in additively (§1-§12 architecture
unchanged):
(1) Enforcement composes onto shipped lance-graph-callcenter::policy
PolicyRewriter chain + PolicyKind taxonomy (RowFilter / ColumnMask /
RowEncryption / DifferentialPrivacy / Audit). The 4-stage authorize()
maps 1:1 onto PolicyKind variants — no parallel enforcement path.
~30% Tier A LOC reduction.
(2) Cross-tenant federation upgraded: A (PureWall) + B (KAnonymity) +
C (EncryptedViewAggregate) all accepted. Option C lifted from 2027+
R&D track to viable now via LanceDB transparent encrypted views —
the engine scans/filters/aggregates over encrypted columns without
decrypting full rows.
(3) Audit chain integrity built-in via shipped MerkleRoot::from_fingerprint
+ ClamPath from graph/spo/merkle.rs. AuditEntry carries merkle_root +
clam_path + super_domain_salt; HIPAA reviewers detect post-hoc tampering
because the merkle would not validate.
(4) Hard-lock requirement formalized: Healthcare ↔ OSINT (and 3 other
pairs) get 3 layers of cryptographic defense — predicate-time rejection +
per-super-domain merkle salt + super-domain-scoped HKDF key derivation.
Patient history and OSINT cannot be jointly queried under any role; a
leaked row decrypts only with both tenant DEK AND super-domain context.
(5) researcher role hardened to anonymized-projection-only:
PermissionSet::READ only, no WRITE/EXPORT/REDACT_LIFT, k-anonymity floor
(k≥5 default; per-super-domain override for rare-condition Healthcare
research), DP noise auto-injected on aggregates via PolicyKind::
DifferentialPrivacy.
New deliverables: D-SDR-13 (per-SD merkle salt + HKDF), D-SDR-14
(updated AuditEntry + tamper-detect replay), D-SDR-15 (DP for researcher),
D-SDR-16 (EncryptedViewAggregate federation), D-SDR-17 (hard-lock
partner matrix enforcement).
Resolved open questions: audit format choice + cross-tenant federation.
New open questions: hard-lock partner matrix completeness + per-SD DP
epsilon defaults + merkle salt rotation cadence + per-SD k-anonymity
floor overrides.
INTEGRATION_PLANS.md correction line appended per APPEND-ONLY governance.
**Correction (2026-05-13):** §13 refinements added (same session). (a) Enforcement composes onto shipped `lance-graph-callcenter::policy::PolicyRewriter` chain + `PolicyKind` taxonomy (RowFilter/ColumnMask/RowEncryption/DifferentialPrivacy/Audit) rather than introducing parallel path — ~30% Tier A LOC reduction. (b) Cross-tenant federation upgraded to A+B+C all accepted; Option C (`EncryptedViewAggregate`) viable now via LanceDB transparent encrypted views, not 2027+ R&D. (c) Audit chain integrity built-in via `MerkleRoot::from_fingerprint` + `ClamPath` from `graph/spo/merkle.rs` (the merkle/DN-path mixing already shipped). (d) Hard-lock requirement formalized: Healthcare ↔ OSINT (and 3 other pairs) get 3 layers of defense — predicate + per-super-domain merkle salt + super-domain-scoped HKDF key derivation. (e) `researcher` role hardened to anonymized-projection-only with k-anonymity floor + DP noise injection on aggregates. New deliverables D-SDR-13..17 added. Open questions on audit format + cross-tenant federation RESOLVED; new open questions on hard-lock partner matrix + per-super-domain DP epsilon + merkle salt rotation cadence.
Copy file name to clipboardExpand all lines: .claude/plans/super-domain-rbac-tenancy-v1.md
+180Lines changed: 180 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -577,3 +577,183 @@ A is the conservative HIPAA-defensible default. B is the practical compromise fo
577
577
## 12 — One-line summary
578
578
579
579
> 4-level hierarchy (meta-anchor → super domain → OGIT basin → slot), 6 bytes per row (4-byte tenant + 2-byte OWL identity), inline per-family codebook with label+schema+verbs, single masked predicate enforces tenant + super-domain + role + slot in one DataFusion vector pass. Foundry parity at the enforcement surface, sub-microsecond hot path.
580
+
581
+
---
582
+
583
+
## 13 — Refinements (2026-05-13, same session)
584
+
585
+
Post-draft user feedback surfaced four substrate facts and two requirement upgrades. All folded in as additive corrections — the §3 DTOs, §8 deliverables, and §12 summary remain valid; this section makes the underlying compositor explicit and tightens the federation + hard-lock policy.
586
+
587
+
### 13.1 The compositor is already shipped: `lance-graph-callcenter::policy`
588
+
589
+
The 4-stage `UnifiedBridge::authorize()` (§3.9) is **not** a new enforcement layer — it composes against `lance-graph-callcenter/src/policy.rs`'s shipped `PolicyRewriter` trait + `PolicyKind` taxonomy:
**Consequence:** Tier A deliverables (D-SDR-1..5) **wire onto the existing `PolicyRewriter` chain** rather than introducing a parallel enforcement path. ~30% LOC reduction on Tier A. The DataFusion `OptimizerRule` machinery already handles the predicate-vector composition described in §3.10.
612
+
613
+
### 13.2 LanceDB transparent encryption upgrades Option C from R&D to viable
614
+
615
+
Earlier framing of cross-tenant federation (§6) classified Option C (homomorphic-encryption aggregate) as a 2027+ R&D track. **Correction:** LanceDB ships **transparent encrypted views** at the column level — the engine scans/filters/aggregates over encrypted columns without decrypting full rows, with key access gated by tenant DEK. This is the substrate Option C needs without bespoke FHE primitives.
616
+
617
+
**Updated federation policy:**
618
+
619
+
```rust
620
+
#[repr(u8)]
621
+
pubenumFederationPolicy {
622
+
PureWall=0, // default — no cross-tenant queries
623
+
KAnonymityAggregate=1, // Phase 2 — k ≥ 5 via PolicyKind::DifferentialPrivacy
**Tier E (D-SDR-12) scope expands:** ship A+B together as Phase 2; add Phase 3 EncryptedViewAggregate path that lifts the k-anonymity threshold for tenants whose data column is encrypted at rest with their own DEK (the engine aggregates over ciphertext when the operation is sum/count/avg with bounded sensitivity).
**The merkle/DN-path mixing the user remembered is here** — `MerkleRoot` stamps content, `ClamPath` carries the hierarchical address (the same shape as `DnPath` from `lance-graph-callcenter::dn_path`).
648
+
649
+
**Wire into the spec:**
650
+
651
+
-**Audit chain integrity:** every `AuditEntry` (Tier D, D-SDR-10) carries the `MerkleRoot` of the row at access time. A second access produces a new merkle root; the audit log records both, so HIPAA reviewers can detect post-hoc tampering (the merkle would not validate against the recorded fingerprint).
652
+
-**Hard-lock attestation (§13.4):** the cryptographic separation between Healthcare and OSINT super domains is attested by **distinct merkle root salts per super domain**. A row whose merkle root validates against the OSINT salt cannot validate against the Healthcare salt, so a leaked row is provably mis-routed at integrity-check time even if the predicate filter is misconfigured.
653
+
654
+
**Updated `AuditEntry` shape** (Tier D refinement):
655
+
656
+
```rust
657
+
pubstructAuditEntry {
658
+
pubtenant:TenantId,
659
+
pubsuper_domain:SuperDomain,
660
+
pubactor_role:&'staticstr,
661
+
pubowl:OwlIdentity,
662
+
pubop:u8, // PermissionSet bit
663
+
pubmerkle_root:MerkleRoot, // NEW: fingerprint at access time
**HIPAA compliance and clinical staff trust require a guarantee stronger than predicate filtering between patient history and OSINT.** The user's framing: "doctors will want to know that patient history and OSINT are hard lock."
673
+
674
+
**Updated DTO:**
675
+
676
+
```rust
677
+
pubstructSuperDomainEntry {
678
+
// ... fields as in §3.4 ...
679
+
pubmerkle_salt:u64, // NEW: per-super-domain integrity salt
|`Science` (when ITAR-tagged) |`[OSINT]` (export control vs intel) |
692
+
693
+
**Enforcement mechanism (3 layers of defense):**
694
+
695
+
1.**Predicate-time:**`authorize()` rejects any query whose `super_domain_target` is in the actor's `hard_lock_partners` list, even if the actor has the source super domain authorized.
696
+
2.**Integrity-time:** different `merkle_salt` per super domain. A misconfigured query that bypasses (1) cannot validate cross-domain merkle roots.
697
+
3.**Encryption-time:** rows in a hard-locked super domain are encrypted with super-domain-scoped key derivation (per-tenant DEK × per-super-domain HKDF info string). A leaked row decrypts only with both the tenant DEK *and* the super-domain context — neither alone suffices.
698
+
699
+
**Sales narrative refresh:**
700
+
701
+
> "Patient history and OSINT are hard-locked. Three layers of defense — predicate, merkle salt, key derivation. A clinician's bridge cannot construct a query that joins patient records with intel; the optimizer rejects it, the merkle would not validate, and the encryption keys won't combine. HIPAA reviewers see a cryptographically attested separation, not a policy promise."
702
+
703
+
### 13.5 Research role: anonymized projection only
704
+
705
+
**The `researcher` role from §4.3 is upgraded to a hard requirement, not a configuration knob.** Per user: "research using anonymized."
706
+
707
+
**Updated `researcher` role definition:**
708
+
709
+
```rust
710
+
RoleGroup {
711
+
role_name:"researcher",
712
+
permissions:PermissionSet(PermissionSet::READ), // no WRITE, no EXPORT, no REDACT_LIFT
713
+
clearance_floor:ClearanceLevel(1), // Restricted; never elevated
714
+
audit_required:true, // every access logged with k-anonymity check
715
+
redaction_mask:FieldRedactionMask {
716
+
readable_slots:BIT_SET_DEIDENTIFIED_ONLY, // only de-identified slots visible
717
+
writable_slots:BitSet256([0; 4]), // empty — researchers never write
718
+
redacted_slots:BIT_SET_DIRECT_IDENTIFIERS, // name, SSN, DOB, MRN, address — all hashed
719
+
},
720
+
},
721
+
```
722
+
723
+
**Composes with `PolicyKind::DifferentialPrivacy`:** when the researcher role queries an aggregate, the optimizer chain auto-injects DP noise per the differential-privacy parameter `ε` configured at the super-domain level (`SuperDomainEntry.dp_epsilon: f32`, NEW field).
724
+
725
+
**Three additive constraints for the researcher role:**
726
+
727
+
1.**Field-level:** direct identifiers always hashed (k-anonymity-style pseudonymization).
728
+
2.**Row-level:** queries over <k=5 rows error out with `RbacError::KAnonymityViolation` rather than returning thin slices.
729
+
3.**Aggregate-level:** when the federated-aggregation gate (§13.2) is enabled, cross-tenant aggregates pass through the encrypted-view path — researcher never sees any tenant's raw values, even pseudonymized.
730
+
731
+
### 13.6 Net architecture diff vs §1-§12
732
+
733
+
| Aspect | §1-§12 baseline | §13 refinement |
734
+
|---|---|---|
735
+
| Enforcement mechanism | New 4-stage `authorize()`| Composes onto shipped `PolicyRewriter` chain in `lance-graph-callcenter::policy`|
736
+
| Federation | A+B accepted, C deferred to 2027+ | A+B+C all accepted; C uses LanceDB transparent encrypted view |
737
+
| Audit format | TBD (open question) |`AuditEntry` carries `MerkleRoot + ClamPath + super_domain_salt`; tamper-detection built in |
-**D-SDR-16** — `EncryptedViewAggregate` federation policy: LanceDB transparent encrypted view bridge for cross-tenant aggregate. ~200 LOC + 4 integration tests against an actual encrypted column.
757
+
-**D-SDR-17** — Hard-lock partner matrix as static table + predicate-time enforcement in `authorize()`. ~60 LOC + 4 tests covering each documented pair.
758
+
759
+
**Status:** Refinements are additive to the §1-§12 architecture. No prior DTO removed; all existing fields stay. Merkle/audit/hard-lock weave through the existing 4-stage flow as policy-rewriter composition rather than parallel paths.
0 commit comments