brainiac-cli 0.16.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- brain/__init__.py +39 -0
- brain/__main__.py +14 -0
- brain/_assets/AGENTS.md +564 -0
- brain/_assets/overlay/template/brand/brand-guide.md +24 -0
- brain/_assets/overlay/template/keywords/glossary.md +15 -0
- brain/_assets/overlay/template/people/roster.md +14 -0
- brain/_assets/overlay/template/voice/voice-profile.md +34 -0
- brain/_assets/routines/manifest.json +257 -0
- brain/_assets/scripts/brain-brief-mac.plist +59 -0
- brain/_assets/scripts/brain-brief.sh +74 -0
- brain/_assets/scripts/brain-synthesis-mac.plist +48 -0
- brain/_assets/scripts/brain-synthesis.sh +214 -0
- brain/_assets/scripts/install-brief-mac.sh +152 -0
- brain/_assets/scripts/install-brief-windows.ps1 +97 -0
- brain/_assets/scripts/register_tasks.py +386 -0
- brain/_assets/templates/company.md +21 -0
- brain/_assets/templates/concept.md +27 -0
- brain/_assets/templates/daily.md +20 -0
- brain/_assets/templates/decision.md +42 -0
- brain/_assets/templates/meeting.md +33 -0
- brain/_assets/templates/person.md +20 -0
- brain/_assets/templates/project.md +23 -0
- brain/_assets/templates/state-moc.md +40 -0
- brain/_version.py +12 -0
- brain/anchor.py +121 -0
- brain/audit.py +422 -0
- brain/backup.py +210 -0
- brain/brief.py +417 -0
- brain/capture.py +117 -0
- brain/chunk.py +249 -0
- brain/classification.py +134 -0
- brain/cli.py +1906 -0
- brain/config.py +368 -0
- brain/connect.py +362 -0
- brain/context.py +108 -0
- brain/core.py +3018 -0
- brain/doctor.py +1161 -0
- brain/egress.py +148 -0
- brain/embed.py +857 -0
- brain/encryption.py +217 -0
- brain/frontmatter.py +102 -0
- brain/golden_probe.py +678 -0
- brain/graph.py +369 -0
- brain/graphify.py +352 -0
- brain/index.py +1576 -0
- brain/ingest/__init__.py +19 -0
- brain/ingest/handlers/__init__.py +43 -0
- brain/ingest/handlers/base.py +95 -0
- brain/ingest/handlers/docx.py +78 -0
- brain/ingest/handlers/email.py +228 -0
- brain/ingest/handlers/html.py +142 -0
- brain/ingest/handlers/image.py +91 -0
- brain/ingest/handlers/pdf.py +99 -0
- brain/ingest/handlers/pptx.py +69 -0
- brain/ingest/handlers/tables.py +41 -0
- brain/ingest/handlers/text.py +43 -0
- brain/ingest/handlers/xlsx.py +100 -0
- brain/ingest/handlers/zip.py +163 -0
- brain/ingest/pipeline.py +839 -0
- brain/ingest/transcript.py +158 -0
- brain/init.py +870 -0
- brain/maintenance.py +2266 -0
- brain/mcp_adapter.py +217 -0
- brain/multihop.py +232 -0
- brain/notes.py +195 -0
- brain/overlay.py +183 -0
- brain/projection.py +79 -0
- brain/rerank.py +425 -0
- brain/snapshot.py +231 -0
- brain/update.py +743 -0
- brain/vectors.py +225 -0
- brainiac_cli-0.16.0.dist-info/METADATA +306 -0
- brainiac_cli-0.16.0.dist-info/RECORD +77 -0
- brainiac_cli-0.16.0.dist-info/WHEEL +5 -0
- brainiac_cli-0.16.0.dist-info/entry_points.txt +4 -0
- brainiac_cli-0.16.0.dist-info/licenses/LICENSE +202 -0
- brainiac_cli-0.16.0.dist-info/top_level.txt +1 -0
brain/egress.py
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""The single egress chokepoint (SEC-01).
|
|
2
|
+
|
|
3
|
+
Every content-returning surface — the CLI subcommands and the optional MCP
|
|
4
|
+
adapter — funnels its results through ONE helper here before stdout, so the
|
|
5
|
+
deny-by-default classification gate cannot be silently bypassed by a *new*
|
|
6
|
+
subcommand that forgets to filter. This is the "force all integration through the
|
|
7
|
+
gated boundary" leg of the SEC-01 hardening (r2-codex): a later
|
|
8
|
+
``graph-expand`` / ``bases-query`` path must not surface a tier a sibling path
|
|
9
|
+
already withholds.
|
|
10
|
+
|
|
11
|
+
This module is an egress *decision* mechanism, NOT containment — a file-capable
|
|
12
|
+
harness reads the Markdown directly and bypasses it entirely (proven by
|
|
13
|
+
tests/test_direct_file_read.py). Real containment of sensitive tiers is
|
|
14
|
+
workspace *projection* (brain.projection) + the host/VM trust split. Per the
|
|
15
|
+
vault's own C-3 doctrine, a CLI/prompt-layer filter is defence-in-depth, never
|
|
16
|
+
the gate. See docs/operations/egress-provider-posture.md.
|
|
17
|
+
"""
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import json
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import Any, Iterable
|
|
23
|
+
|
|
24
|
+
from . import classification as cls
|
|
25
|
+
|
|
26
|
+
# CANONICAL enumeration of every content-returning subcommand (r2-codex).
|
|
27
|
+
# Egress/classification-gate test coverage MUST cover each of these — a content
|
|
28
|
+
# path that is not on this list and not gated is a posture gap. ``rerank`` is not
|
|
29
|
+
# a separate command: ``search --rerank`` re-orders the SAME hits, then the same
|
|
30
|
+
# gate fires (covered explicitly in the test).
|
|
31
|
+
CONTENT_RETURNING_SUBCOMMANDS: tuple[str, ...] = (
|
|
32
|
+
"search", # fused RRF BM25+dense
|
|
33
|
+
"hybrid-search", # alias of search
|
|
34
|
+
"grep", # lexical scan
|
|
35
|
+
"bases-query", # structured frontmatter view
|
|
36
|
+
"graph-expand", # wikilink-BFS + PPR discovery candidates
|
|
37
|
+
"get", # one note by id
|
|
38
|
+
"read", # alias of get
|
|
39
|
+
"recent", # recently-updated list
|
|
40
|
+
# CUT-03 maintenance rituals that surface note-id+classification listings
|
|
41
|
+
# (curate's unclassified-notes lint, integrity's near-dup pairs, promote-
|
|
42
|
+
# scan's raw/ candidates) route through the SAME egress.apply_gate
|
|
43
|
+
# chokepoint as the read verbs above — brain-cli-gaps.md G1 explicitly
|
|
44
|
+
# requires both members of a near-dup pair to be gated before surfacing.
|
|
45
|
+
"curate", "integrity", "promote-scan",
|
|
46
|
+
# UX-02 summary surfaces (H-1) — brief/digest build their note list from
|
|
47
|
+
# the SAME recent() feed as `recent` above, so they route through the
|
|
48
|
+
# SAME apply_gate chokepoint before that list is assembled into the
|
|
49
|
+
# brief/digest structure. See BrainCore.brief / BrainCore.digest.
|
|
50
|
+
"brief", "digest",
|
|
51
|
+
# GRF-01 (ADR-0003 Ruling 6/(a)) — graphify's INFERRED link candidates
|
|
52
|
+
# name note ids the same way graph-expand's discovery candidates do;
|
|
53
|
+
# gated the SAME way (both endpoints of a candidate pair must clear the
|
|
54
|
+
# cap) before they reach the CLI output or a maintain hot-queue entry.
|
|
55
|
+
"graphify",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Host-broker / maintenance commands return STATUS, never note bodies — they are
|
|
59
|
+
# intentionally NOT gated by classification (nothing to leak) and so are NOT in
|
|
60
|
+
# the list above. Listed here for the audit so the split is explicit.
|
|
61
|
+
NON_CONTENT_SUBCOMMANDS: tuple[str, ...] = (
|
|
62
|
+
"draft-capture", "rebuild", "sync", "snapshot", "status", "project",
|
|
63
|
+
"write", "verify-audit", "anchor", "verify-anchor", "backup", "restore",
|
|
64
|
+
"check", "health", "maintain",
|
|
65
|
+
# TMP-02: two note ids + a status/audit summary — no note bodies returned.
|
|
66
|
+
"supersede",
|
|
67
|
+
# SUI-02: per-client wiring status (diff/confirm/write report) — no note
|
|
68
|
+
# bodies, no BrainCore construction at all. Host-only (refused at the
|
|
69
|
+
# VM_ALLOWED gate in cli.py before this command ever dispatches).
|
|
70
|
+
"connect",
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def apply_gate(
|
|
75
|
+
items: Iterable[dict], max_tier: str = cls.DEFAULT_MAX_TIER,
|
|
76
|
+
key: str = "classification",
|
|
77
|
+
) -> tuple[list[dict], dict]:
|
|
78
|
+
"""THE chokepoint: deny-by-default filter + honest redaction report.
|
|
79
|
+
|
|
80
|
+
Returns ``(surfaced, egress_report)``. Used by the CLI for every
|
|
81
|
+
content-returning subcommand and by the MCP adapter — one code path, no
|
|
82
|
+
second egress surface to keep in sync.
|
|
83
|
+
"""
|
|
84
|
+
items = list(items)
|
|
85
|
+
flt = cls.ClassificationFilter(max_tier=max_tier)
|
|
86
|
+
return flt.filter(items, key=key), flt.redaction_report(items, key=key)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# --------------------------------------------------------------------------
|
|
90
|
+
# Trusted-harness allowlist (SEC-01, HARDENED:claude / r2-claude)
|
|
91
|
+
# --------------------------------------------------------------------------
|
|
92
|
+
# Reconciles openness vs control. "Openness" does NOT mean "any app" — it means
|
|
93
|
+
# any harness that PASSES the vendor-posture bar (no-train/ZDR scope covering
|
|
94
|
+
# tool-call/API egress of vault content + MNPI). The bar is owned here as a
|
|
95
|
+
# gating checklist; val-03's cross-harness set must EQUAL this allowlist. The
|
|
96
|
+
# register itself is data (docs/harness-allowlist.json) so it is reviewable and
|
|
97
|
+
# diffable; this loader is the typed accessor + invariants.
|
|
98
|
+
ALLOWLIST_PATH = Path(__file__).resolve().parents[2] / "docs" / "harness-allowlist.json"
|
|
99
|
+
|
|
100
|
+
_POSTURE_STATES = frozenset({"VERIFIED", "PENDING", "REJECTED"})
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def load_allowlist(path: Path | None = None) -> dict[str, Any]:
|
|
104
|
+
"""Load the trusted-harness allowlist register. Raises on a malformed file
|
|
105
|
+
(fail-closed: a register we cannot parse must not be treated as 'allow all')."""
|
|
106
|
+
p = Path(path) if path else ALLOWLIST_PATH
|
|
107
|
+
data = json.loads(p.read_text(encoding="utf-8"))
|
|
108
|
+
if "harnesses" not in data or not isinstance(data["harnesses"], list):
|
|
109
|
+
raise ValueError(f"allowlist {p} missing a 'harnesses' list")
|
|
110
|
+
for h in data["harnesses"]:
|
|
111
|
+
missing = {"id", "vendor", "posture_status", "verification_step", "owner"} - set(h)
|
|
112
|
+
if missing:
|
|
113
|
+
raise ValueError(f"allowlist entry {h.get('id')!r} missing keys: {sorted(missing)}")
|
|
114
|
+
if h["posture_status"] not in _POSTURE_STATES:
|
|
115
|
+
raise ValueError(
|
|
116
|
+
f"allowlist entry {h['id']!r} has posture_status "
|
|
117
|
+
f"{h['posture_status']!r}; expected one of {sorted(_POSTURE_STATES)}"
|
|
118
|
+
)
|
|
119
|
+
return data
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def is_allowed(harness_id: str, path: Path | None = None) -> bool:
|
|
123
|
+
"""True iff ``harness_id`` is on the allowlist AND its vendor posture is
|
|
124
|
+
VERIFIED. PENDING/REJECTED => default-deny (the posture bar is not met).
|
|
125
|
+
|
|
126
|
+
NOTE: the brain CLI cannot reliably identify its caller, so this is a
|
|
127
|
+
GOVERNANCE gate (consumed by val-03 + the cyber review + deployment policy),
|
|
128
|
+
not a runtime per-request gate. The runtime control is the classification
|
|
129
|
+
gate (apply_gate) + projection. Until a vendor's no-train/ZDR scope is
|
|
130
|
+
contractually VERIFIED, the harness is not 'allowed' and must run only
|
|
131
|
+
against a projected (sensitive-tier-free) workspace.
|
|
132
|
+
"""
|
|
133
|
+
data = load_allowlist(path)
|
|
134
|
+
for h in data["harnesses"]:
|
|
135
|
+
if h["id"] == harness_id:
|
|
136
|
+
return h["posture_status"] == "VERIFIED"
|
|
137
|
+
return False
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def posture_summary(path: Path | None = None) -> dict[str, Any]:
|
|
141
|
+
"""Counts by posture_status for the evidence table / CSF profile."""
|
|
142
|
+
data = load_allowlist(path)
|
|
143
|
+
out: dict[str, int] = {s: 0 for s in _POSTURE_STATES}
|
|
144
|
+
for h in data["harnesses"]:
|
|
145
|
+
out[h["posture_status"]] += 1
|
|
146
|
+
return {"total": len(data["harnesses"]), "by_status": out,
|
|
147
|
+
"verified_ids": [h["id"] for h in data["harnesses"]
|
|
148
|
+
if h["posture_status"] == "VERIFIED"]}
|