Skip to content

feat(agents): enforce trusted heartbeat source preflight - #5602

Open
Greg-Moskalenko wants to merge 6 commits into
block:mainfrom
Greg-Moskalenko:build/trusted-heartbeat-preflight-v2
Open

feat(agents): enforce trusted heartbeat source preflight#5602
Greg-Moskalenko wants to merge 6 commits into
block:mainfrom
Greg-Moskalenko:build/trusted-heartbeat-preflight-v2

Conversation

@Greg-Moskalenko

@Greg-Moskalenko Greg-Moskalenko commented Aug 11, 2026

Copy link
Copy Markdown

Summary

  • add a typed, owner-authoritative heartbeat preflight designation to managed agents and preserve it across update, snapshot, restore, stop, delete, and rollback paths
  • run a trusted gateway capability before a designated heartbeat reaches the model, failing closed on incomplete, stale, malformed, or replayed source receipts
  • bind the macOS harness to a protected path, exact executable identity, hardened runtime, empty entitlements, and a statically readable capability
  • support both the existing Apple Developer ID trust path and an exact one-Mac local-certificate leaf pin without weakening identifier, runtime, entitlement, resource-seal, or replacement checks
  • serialize designation edits with runtime transitions so an obsolete process cannot survive a trust-policy change
  • harden release and canary workflows so the exact validated artifact remains bound through packaging and publication

The bug is structural: prompt text can ask an agent to call every source, but it cannot prove those calls happened before the agent reports a no-op. This moves that decision to a pre-agent runtime gate and makes incomplete source coverage suppress the model turn.

Key safety properties

  • designated runs require a fresh harness invocation ID and the exact configured gateway capability
  • the model sees only committed, sanitized gateway results after preflight acceptance
  • a pinned lowercase owner must match the owner resolved from the verified auth tag before any external runtime activity
  • helper descendants are terminated on every terminal outcome; unsupported platforms fail closed
  • concurrent restore/update/start/stop/delete/rollback paths cannot register a stale or unprotected child
  • local certificate mode pins the exact SHA-1 certificate leaf and fixed identifier; Developer ID behavior remains unchanged
  • macOS packages embed the exact capability scalar in signature-covered Mach-O bytes
  • ACL, executable, signature, runtime, entitlement, resource-seal, and pre-spawn replacement checks remain fail closed

Testing

  • env -u BUZZ_ACP_LAZY_POOL ./bin/just ci passed on exact head 2b1ceed9c1e449a75ba4ed5c9148b38a20ae4285
  • repository formatting, Clippy, desktop/web/mobile analysis, file-size gates, production builds, and full unit suites passed
  • 2,442 Tauri library tests passed, plus all Tauri integration and terminal tests
  • 4,792 desktop tests and the full Flutter suite passed
  • focused local-certificate parser, signature requirement, runtime replacement, and configuration-transition tests passed
  • git diff --check passed
  • branch includes current upstream main at df9e773a13f17a270fd6531fc74948b8059d58c3; GitHub reports it mergeable

Manual testing / screenshots

No new end-user UI is introduced, so screenshots are not applicable. A production acceptance run still requires the separately deployed gateway, source credentials/helpers, and fresh signed source receipts; the feature deliberately fails closed until those are present.

Deferred follow-up

  • complete upstream maintainer review and protected CI, then merge through the ordinary Apps release path
  • build and install the trusted gateway from the reviewed exact harness commit
  • provision isolated source credentials/helpers through owner-held permission flows
  • complete two fresh scheduled all-source runs with accepted readback

Co-authored-by: Greg Moskalenko <greg1mosk@gmail.com>
Signed-off-by: Greg Moskalenko <greg1mosk@gmail.com>
@Greg-Moskalenko
Greg-Moskalenko requested a review from a team as a code owner August 11, 2026 21:19

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 939b524723

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

use std::os::unix::ffi::OsStrExt;

type Acl = *mut c_void;
unsafe extern "C" {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Remove the unsafe ACL FFI

This introduces an unsafe extern block and several unsafe calls for ACL inspection, despite the repository’s explicit prohibition on any unsafe code. Replace this implementation with a safe wrapper or command-based ACL inspection so the macOS heartbeat verification path complies with the project’s required safety invariant.

AGENTS.md reference: AGENTS.md:L114-L116

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 8589050c9. The macOS verifier no longer uses FFI or any unsafe code. It now performs fail-closed ACL inspection through /bin/ls -lde with a cleared environment, bounded output, strict status/stderr/UTF-8 checks, and an ACL regression test. The full just ci gate passes on this exact head.

};

// ── Pre-Phase 2: validate provider config BEFORE any side effects ────────
input.validate_heartbeat_preflight(&pubkey)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Expose the generated pubkey before validating its policy

When heartbeatPreflight is supplied during creation, the target pubkey is generated immediately above this call and is never exposed to the caller, but policy validation requires target_agent_pubkey to equal that newly generated value. A caller therefore cannot prepare a valid policy in advance, and retrying after failure generates another key, making the new create-time designation path effectively unusable; allocate or expose the key before accepting the policy, or require designation through the subsequent update path.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 8589050c9. Create requests now reject a heartbeat designation before any side effect, the TypeScript create surface no longer exposes that field, and designation is required through update_managed_agent after creation returns the agent pubkey. The new boundary test and the full just ci gate pass on this exact head.

Require heartbeat designation through the post-create update path once the agent pubkey exists. Replace the macOS ACL FFI with fail-closed system ACL inspection so the trusted harness path contains no unsafe Rust.

Co-authored-by: Greg Moskalenko <greg1mosk@gmail.com>
Signed-off-by: Greg Moskalenko <greg1mosk@gmail.com>
@Greg-Moskalenko

Copy link
Copy Markdown
Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

Reviewed commit: 8589050c9c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@Greg-Moskalenko

Copy link
Copy Markdown
Author

Current head 8589050 addresses both automated review findings and passes the full local just ci gate. Semgrep, zizmor, and DCO are green. The three fork workflows are still action_required; could a maintainer approve those workflows and review the current head?

Co-authored-by: Greg Moskalenko <greg1mosk@gmail.com>
Signed-off-by: Greg Moskalenko <greg1mosk@gmail.com>
Bind local macOS trust to the exact broker identifier and certificate leaf fingerprint while preserving Developer ID TeamIdentifier verification.

Co-authored-by: Greg Moskalenko <greg1mosk@gmail.com>
Signed-off-by: Greg Moskalenko <greg1mosk@gmail.com>
Resolve the managed-agent creation and model-update overlap while retaining the owner-bound preflight gate.

Co-authored-by: Greg Moskalenko <greg1mosk@gmail.com>
Signed-off-by: Greg Moskalenko <greg1mosk@gmail.com>
Co-authored-by: Greg Moskalenko <greg1mosk@gmail.com>
Signed-off-by: Greg Moskalenko <greg1mosk@gmail.com>

Copy link
Copy Markdown
Author

@codex review

Current head is rebased through upstream main, mergeable, and passes the full local just ci gate. Please review the exact current head.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2b1ceed9c1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +98 to +100
.and_then(|value| nostr::Keys::parse(value).ok())
.map(|keys| keys.public_key());
let resolved_owner = agent_public_key.as_ref().and_then(|agent_pubkey| {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Resolve the helper owner fallback without a private key

When a helper subcommand runs with matching BUZZ_ACP_REQUIRED_AGENT_OWNER and BUZZ_ACP_AGENT_OWNER but no BUZZ_PRIVATE_KEY, agent_public_key is None, so this and_then skips the configured-owner fallback entirely and models, auth-methods, and authenticate all fail the latch. Those discovery/auth helpers do not otherwise require an agent key, and this also breaks Desktop helper probes inheriting an operator-configured latch; consult configured_owner independently and require the private key only when verifying an auth tag.

Useful? React with 👍 / 👎.

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.

1 participant