Skip to content

BAT Protocol: deterministic governance layer and security hardening#3

Open
Knapp-Kevin wants to merge 12 commits intossdavidai:masterfrom
Knapp-Kevin:Bat-Protocol
Open

BAT Protocol: deterministic governance layer and security hardening#3
Knapp-Kevin wants to merge 12 commits intossdavidai:masterfrom
Knapp-Kevin:Bat-Protocol

Conversation

@Knapp-Kevin
Copy link
Copy Markdown

BAT Protocol: Deterministic Governance Layer (End-to-End)

Summary

This PR delivers the full BAT Protocol governance surface in alfred.bat, including deterministic risk classification, policy enforcement, integrity-protected audit logging, security elevation tracks, enterprise governance modules, and operator tooling.

Core invariant: no agent decides its own risk.

Why

Alfred agents can execute commands, mutate files, and access sensitive runtime capabilities. BAT introduces a proposal-first governance path so every operation is classified, enforced, and auditable under deterministic policy.

What�s Included

Foundation and Hardening

  • Canonical operation proposal model and deterministic risk classification.
  • Policy-driven enforcement engine with fail-closed behavior.
  • Append-only HMAC-signed governance ledger.
  • Path hardening, temporal risk accumulation, break-glass control flow, and governance profiles.
  • BAT operator CLI commands:
    • bat status
    • bat audit
    • bat test-policy
    • bat verify-ledger
    • bat explain

Security Elevation Tracks

  • Identity verification with personal, secure, and enterprise modes.
  • Delegation with bounded capability chain controls.
  • Policy integrity guard and startup readiness checks.
  • Semantic vector governance (ZVEC): artifact verification, drift signals, upgrade lifecycle.
  • Resource and deserialization protections: limits, rate controls, metadata validation, safe loaders.

Enterprise Governance

  • Quorum approval workflows.
  • Policy signing and immutable policy versioning with hash chaining.
  • Remote policy distribution and caching.
  • Multi-node ledger synchronization.
  • Compliance evidence and reporting support.

Security Fixes Included in This PR

  • Fail-closed identity verification when PyNaCl is unavailable in secure/enterprise mode.
  • Ledger synchronization now verifies cryptographic signatures (not only hashes).
  • Ledger entry path traversal protections via strict entry_id validation.
  • Policy signatures now cover status; deprecate/revoke paths re-sign updated records.
  • Ledger sync key handling hardened to support verify-key bytes or signing-seed input safely.

Documentation and Hygiene

  • Added/updated BAT protocol implementation README:
    • README-BATPROTOCOL.md
  • Corrected version claim to 1.5.0.
  • Added .hypothesis/ to .gitignore.

Testing

  • Ran: pytest tests/bat -q
  • Result: 159 passed, 1 skipped
  • Added/updated tests to validate stricter ledger signature verification behavior, including signing-seed compatibility.

Backward Compatibility and Risk

  • Security posture is stricter by design (fail-closed on missing signature verification capability).
  • Existing unsigned or improperly signed sync entries are rejected.
  • Main rollout risk is environment key material mismatch; mitigated by dual key-material handling in ledger sync verification.

Source of Truth

  • Runtime/package surface: src/alfred/bat/__init__.py (__version__ = "1.5.0")
  • Implementation guide: README-BATPROTOCOL.md
  • Roadmap/canonical plan: docs/bat-protocol/PROJECT_PLAN.md

Status Snapshot

  • Implemented: core BAT runtime, security elevation modules, enterprise modules, CLI surface, tests above.
  • Planned (roadmap framing remains): phase sequencing and broader program milestones documented in PROJECT_PLAN.md.

Commit

  • 3e0d676 - Harden BAT verification paths and close signature coverage gaps

@ssdavidai ssdavidai requested a review from Copilot February 25, 2026 16:48
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a full deterministic governance layer (“BAT Protocol”) for agent operations, including proposal normalization, deterministic risk classification, policy enforcement, integrity-protected audit logging, and operator tooling.

Changes:

  • Adds core BAT runtime modules: proposal schema, risk engine + default rules, enforcement engine, interceptor, and HMAC-linked governance ledger.
  • Adds security hardening modules: path validation, policy integrity guard + startup gate, resource governor, delegation, break-glass, and optional ledger encryption.
  • Adds DSL tooling (YAML rule parser/loader) plus CLI commands and documentation updates.

Reviewed changes

Copilot reviewed 43 out of 66 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
src/alfred/bat/rules/init.py Adds baseline deterministic risk rules and helper matchers.
src/alfred/bat/risk.py Implements deterministic risk engine, rules, and explain output.
src/alfred/bat/resource_governor.py Adds rate limits, metadata caps, queue depth checks, rotation, and safe deserialization helpers.
src/alfred/bat/proposal.py Adds canonical OperationProposal schema and validation/hash utilities.
src/alfred/bat/profiles.py Adds personal/secure/enterprise profiles and profile utilities.
src/alfred/bat/policy_integrity.py Adds signed manifest verification and immutable-root protections with a startup gate.
src/alfred/bat/path_security.py Adds path traversal/UNC/ADS/null-byte blocking and optional symlink restrictions.
src/alfred/bat/ledger.py Adds append-only hash-chained HMAC ledger with verification and export/stats.
src/alfred/bat/interceptor.py Adds main governance interception + execution scaffold with invariant re-check hook.
src/alfred/bat/enforcement.py Adds policy-driven enforcement + ledger recording of decisions.
src/alfred/bat/encryption.py Adds optional at-rest ledger encryption support.
src/alfred/bat/dsl/parser.py Adds YAML DSL parser converting rules into executable RiskRules.
src/alfred/bat/dsl/loader.py Adds ruleset loader with checksum-based hot reload and default rules file generator.
src/alfred/bat/dsl/init.py Exposes DSL public API.
src/alfred/bat/delegation.py Adds capability-set delegation with chain provenance and intersection enforcement.
src/alfred/bat/cli.py Adds operator CLI commands (status/audit/test-policy/verify-ledger/explain).
src/alfred/bat/break_glass.py Adds emergency override + break-glass session support with separate logging.
src/alfred/bat/init.py Exposes BAT Protocol public surface and updates version to 1.5.0.
src/alfred/_bundled/config.yaml.example Documents semantic drift feature flags.
pyproject.toml Adds optional dependencies for bat and dev; updates “all” extras.
docs/bat-protocol/_Sidebar.md Adds BAT documentation sidebar and quick start.
docs/bat-protocol/PROJECT_PLAN.md Adds project plan and phase roadmap.
docs/bat-protocol/CANONICAL_DESIGN.md Adds canonical BAT architecture specification.
docs/Surveyor.md Documents semantic drift configuration + artifacts.
docs/CLI-Commands.md Documents new drift CLI commands.
config.yaml.example Documents semantic drift feature flags.
SECURITY_REVIEW.md Adds an automated security review document.
SECURITY_ELEVATION.md Adds BAT security elevation plan and acceptance gates.
README-BATPROTOCOL.md Adds BAT implementation-focused README for contributors.
INITIAL_SECURITY_REVIEW.md Adds drift monitor security review and remediation log.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/alfred/bat/rules/__init__.py Outdated
Comment on lines +127 to +149
def is_within_vault(path: str, vault_root: str = None) -> bool:
"""Check if path is within the vault directory.

Args:
path: File path to check
vault_root: Root directory of the vault (default: ~/vault)

Returns:
True if path is within vault, False otherwise
"""
if not path:
return False

if vault_root is None:
vault_root = os.path.expanduser("~/vault")
else:
vault_root = os.path.expanduser(vault_root)

expanded_path = os.path.expanduser(path)
normalized_path = os.path.normpath(expanded_path)
normalized_vault = os.path.normpath(vault_root)

return normalized_path.startswith(normalized_vault)
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

startswith() is not a safe containment check (e.g., /home/user/vault2/... will be treated as within /home/user/vault). Use Path(...).expanduser().resolve() and a relative_to() containment check (or equivalent) to ensure the target is truly inside the vault root.

Copilot uses AI. Check for mistakes.
Comment thread src/alfred/bat/resource_governor.py Outdated
Comment thread src/alfred/bat/resource_governor.py
Comment thread src/alfred/bat/resource_governor.py Outdated
Comment thread src/alfred/bat/policy_integrity.py Outdated
Comment thread src/alfred/bat/profiles.py Outdated
Comment thread src/alfred/bat/interceptor.py
Comment thread src/alfred/bat/encryption.py Outdated
Comment thread src/alfred/bat/dsl/parser.py Outdated
Comment thread src/alfred/bat/cli.py Outdated
Knapp-Kevin and others added 2 commits February 26, 2026 11:08
Security fixes:
- Fix path traversal in is_within_vault using Path.resolve()/relative_to()
- Fail-closed when PyNaCl missing instead of bypassing signature verification
- Remove insecure XOR fallback cipher when cryptography library unavailable
- Exclude manifest.json from self-referential hashing

Correctness fixes:
- Fix f-string bug: "JSON parsing failed: e" -> "JSON parsing failed: {e}"
- Fix rate limit windows to match documented limits (1s/60s/3600s)
- Fix get_usage to aggregate per-second count across agent_id:* keys
- Fix ProfileName enum handling for invalid/missing values
- Fix create_custom_profile to handle non-standard profile names
- Record BLOCK entry to ledger when invariant check fails (audit consistency)
- Fix lambda closure bug in DSL parser (pats vs patterns)

Performance fix:
- Stream ledger with bounded deque in bat audit for constant memory usage

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Code fixes:
- Move ClusterState and datetime imports to module scope (daemon.py)
- Use .get() with defaults for drift report fields to prevent KeyError (drift_cli.py)
- Add _setup_logging_from_config() call to cmd_drift (cli.py)

Documentation fixes:
- Clarify that features/semantic_drift are top-level config sections (Surveyor.md)
- Fix inaccurate statement about drift commands availability (CLI-Commands.md)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@Knapp-Kevin
Copy link
Copy Markdown
Author

All concerns fully addressed, ready for merge.

ssdavidai added a commit that referenced this pull request Mar 27, 2026
* feat: close Curator ↔ alfred-learn filing loop

Wire the routing brain (alfred-learn) to the processing muscles (Curator):

- New endpoint: POST /api/v1/curator/route-and-process
  - Accepts routing context (destination, instinct, confidence)
  - Injects routing metadata as frontmatter into inbox file
  - Triggers Curator for structured processing (notes, entities, wikilinks)
  - Returns enriched result (note_path, entities_created, entities_linked)
  - Falls back to raw fs.rename if Curator unavailable

- Updated POST /api/v1/learning/route (human routing)
  - Now also triggers Curator for structured processing
  - Observations enriched with Curator output

- Updated alfred-learn judge.py
  - execute_route calls new curator endpoint (120s timeout)
  - attempt_judgment (inline) uses Curator via _execute_route_inline
  - Both fall back gracefully if Curator is down

- Updated alfred-learn judgment.py
  - Passes full routing context (instinct name, confidence, assignee)
  - Observation records include Curator results
  - Avoids duplicate observations when endpoint writes its own

The loop: inputs → classify → score → route (Curator processes with
routing context) → structured records + enriched observation → reflection
learns from richer data → instincts improve. Brain and muscles connected.

* fix: address Copilot review — security, robustness, YAML safety

Fixes all 11 Copilot review comments:

1. YAML escaping: Added yamlEscape() helper for frontmatter values
   containing colons/quotes/newlines. All observation and routing
   frontmatter now properly escaped.

2. Path traversal: Added assertSafePath() — validates vault-relative
   paths have no '..' or absolute components. Called in both endpoints.

3. Inbox filename collision: Unique suffix (timestamp + random) on
   inbox filenames prevents concurrent request overwrites.

4. Curator file verification: After Curator runs, checks if inbox
   file was consumed. If not (processed different file), returns error
   instead of silently deleting original.

5. Fallback error handling: Raw move failures now captured and
   reported. Returns 500 with error details when both Curator AND
   fallback fail (instead of silent 200).

6. observation_path normalization: Legacy /learning/route returns both
   'observation' and 'observation_path' keys. Python fallbacks normalize
   the key. judgment.py checks both keys to prevent duplicate writes.

7. Route failure escalation: judgment.py now escalates to user when
   routing completely fails (both Curator and fallback) instead of
   silently counting as routed.

8. attempt_judgment truthfulness: Only reports routed=True when
   routing actually succeeded (checks processed/fallback/observation).

---------

Co-authored-by: ssd <administrator@administrators-Mac-mini.local>
newtonium-errant added a commit to newtonium-errant/alfred that referenced this pull request Apr 18, 2026
…ray mutation log)

Fifth and final commit for Voice Stage 2a-wk2. Team-lead decision to
bundle the three small polish fixes together (plan open question ssdavidai#4).

(a) Transcript timestamps
- session.append_turn stamps _ts on every turn and _kind on user turns.
  New kind="text" kwarg. Wk1 transcripts collapsed every turn to the
  session start time because _ts was never stamped.

(b) Voice counter cleanup
- conversation.run_turn accepts user_kind="text" and threads it into
  append_turn as kind=.
- bot._open_session_with_stash no longer initialises state-dict
  voice_messages / text_messages counters. handle_message no longer
  increments them. _count_message_kinds (already shipped) is the one
  source of truth, deriving from per-turn _kind.

(c) Stray mutation log (plan open question ssdavidai#3)
- conversation._execute_tool drops both mutation_log.log_mutation(
  session.session_id, ...) calls. That API expects a JSONL FILE PATH,
  not a UUID — wk1 passing session.session_id caused open(<uuid>, "a")
  to create a file literally named after the UUID at the repo root
  (286921d8-* artifact cleaned manually). Session vault_ops are
  already tracked via append_vault_op → session-record frontmatter.
  data/vault_audit.log wiring will pick up the same events later.
  mutation_log import dropped from conversation.py.

Tests: 7 new tests across three files, one per bug class:
- test_session_body.py (2 tests — _ts stamped, body renders distinct HH:MM)
- test_voice_counter.py (3 tests — count function, run_turn threads kind,
  stash doesn't seed redundant counters)
- test_no_orphan_mutation_log.py (2 tests — call-signature guard,
  end-to-end _execute_tool leaves no stray UUID file at CWD)

pytest: 38/38 passing (31 prior + 7 new).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
newtonium-errant added a commit to newtonium-errant/alfred that referenced this pull request Apr 26, 2026
When KAL-LE hits 404 record_not_found on GET /canonical/person/{name},
it now POSTs a proposal to Salem's new /canonical/person/propose
endpoint instead of silent fallback. Handles 409 race (retry GET),
logs structured events for audit. Proposer correlation ID shape:
kal-le-propose-person-<hex6>.

This closes the ssdavidai#3 task design from project_kalle_propose_person.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
newtonium-errant added a commit to newtonium-errant/alfred that referenced this pull request Apr 26, 2026
…, worked-example redirect, near-miss bad example, scholar-tone, mode-name consistency

Targeted fixes from vault-reviewer pass on commit 39a852a:

P0-1: Phase 2 leak removed from vault_edit core instructions. The
published_url clause was sitting in the main use-it list; moved to a
clearly-marked "Phase 2 forward-compat note" block at the bottom of
the section, with explicit "do not act on this in Phase 1" gating and
"draft/essay/ is out of scope — don't touch it."

P0-2: propose-person flow now has the explicit transport CLI form
(mirrors KAL-LE's pattern):
    alfred transport propose-person salem "<Full Name>" [--alias ...] [--note ...]
The earlier in-flow reference (Mode 1 step 3) cross-references the
expanded peer-protocol section instead of saying "escalate via
propose-person" with no command shape.

P1-1: Conversation-mode "good question" ssdavidai#3 — the soft-redirect
"if you wrote that as a one-pager..." example pulled Andrew from
"is this the marketing angle?" into "let's operationalize the
one-pager." Replaced with a question that stays inside the
marketing-angle thesis: "What separates the stories that would
land for a stranger from the ones that only land for the driver
who saw them?"

P1-2: Added one near-miss bad example to the bad-questions list:
"Is the story really the marketing, or is the reliability what
people are buying?" — frame-replacement dressed as a clarifying
question. Annotated as the hardest failure mode to catch in
oneself, with the heuristic shape: "is it really X, or is it Y?"
where Y wasn't on the table = reframing, not deepening.

P1-3: Hard guardrails preamble rewritten in Hypatia's target
voice as self-demonstration (scholar-first per
feedback_practitioner_scholar_calibration.md). The preamble and
the first guardrail's lead now read in a careful, evidence-
respecting register; the procedural detail of the four guardrails
themselves stays imperative.

P1-4: Mode-name consistency — the Tone section bullets used
"Business drafting / Conversation / Capture extraction" while
the rest of the SKILL uses "business mode / conversation mode /
capture mode" with numbered headers ("Mode 1 — ..."). Swept the
Tone section to match. Same pattern as KAL-LE's SKILL.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants