Skip to content

fix(p2p): harden bootstrap trust - #72

Merged
trvon merged 1 commit into
fix/p2p-transport-availabilityfrom
fix/p2p-bootstrap-trust
Aug 30, 2026
Merged

fix(p2p): harden bootstrap trust#72
trvon merged 1 commit into
fix/p2p-transport-availabilityfrom
fix/p2p-bootstrap-trust

Conversation

@trvon

@trvon trvon commented Aug 29, 2026

Copy link
Copy Markdown
Owner

Summary

Stacked draft child of #71. This change closes the disclosed direct-P2P bootstrap-trust and private-key protection blockers without merging or deploying anything.

  • requires a usable schema-v4 writer-authentication manifest for direct transport and rejects unauthenticated embedded P2pManager construction
  • requires explicit operator enrollment on both sides of cold bootstrap; legacy TOFU and connection pins remain non-authoritative
  • migrates peer trust with a new enrollment-provenance generation, invalidating legacy pinned_by_operator bits until explicit re-enrollment
  • fails closed on unsigned/provenance-unknown direct operation stores and documents the fresh-epoch migration procedure
  • verifies direct writer private keys even when TLS uses a separate identity key
  • creates and reads identity/writer keys through same-object owner-only checks: open/fstat/O_NOFOLLOW on Unix and handle-based reparse, owner SID, protected-DACL, and ACE validation on Windows
  • propagates Windows advapi32 linkage to daemon and fuzz consumers

Before the fix:

  • direct configuration without writer authentication was accepted
  • a peer remembered through legacy TOFU could satisfy cold-bootstrap transport trust
  • pre-upgrade connect ?pin= rows could retain false operator authority
  • embedded callers could construct direct P2pManager instances over unsigned schema-v3 services
  • existing unsigned stores were quarantined silently instead of producing an actionable upgrade failure
  • separate writer signing keys bypassed the TLS identity key's owner-only permission checks
  • Windows key creation inherited ambient ACL authority and pathname validation raced the later read

Known stack blockers

This draft child is not independently mergeable or deployable. The umbrella still has separately tracked replicated-apply serialization, operator-status semantics, fuzzing, and final live gates.

AI disclosure

Extensive AI assistance was used for implementation, test generation, and adversarial review. All changes were locally inspected and validated; remaining stack blockers are disclosed above.

Stack

Do not merge or deploy this child independently.

Require authenticated direct stores and explicit operator enrollment while hardening private keys and trust files across Unix and Windows. Sign local control artifacts, bind them to a protected store generation, and serialize async initialization shutdown.

Signed-off-by: trvon <git@trevon.dev>
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying yams with  Cloudflare Pages  Cloudflare Pages

Latest commit: 435ff22
Status: ✅  Deploy successful!
Preview URL: https://40dad1e1.yams.pages.dev
Branch Preview URL: https://fix-p2p-bootstrap-trust.yams.pages.dev

View logs

@trvon
trvon marked this pull request as ready for review August 30, 2026 15:48
Copilot AI lite review requested due to automatic review settings August 30, 2026 15:48

Copilot AI 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.

🔵 Needs a closer look

It changes security-critical trust, migration, and platform-specific ACL/key-handling logic across multiple subsystems and should receive final human review.

Pull request overview

Hardens direct P2P bootstrap trust and key handling by requiring writer-authenticated operation stores and explicit operator enrollment, with migration behavior that fails closed on legacy/unsigned history. This fits into the daemon’s direct replication path (memory_sync + p2p manager/protocol) and tightens platform-specific filesystem/ACL enforcement.

Changes:

  • Require usable writer-auth manifests for direct transport and propagate authenticated “control artifacts” (journals/checkpoints/history) with signed provenance and a per-store control scope.
  • Make cold bootstrap require explicit operator enrollment and migrate peer trust with an enrollment-generation gate that invalidates legacy pinned_by_operator authority.
  • Add Windows key/ACL hardening and ensure advapi32 linkage is available to daemon and fuzzing consumers.
File summaries
File Description
tools/fuzzing/meson.build Links advapi32 on Windows for fuzzing targets that now depend on ACL APIs.
tests/unit/memory_sync/memory_sync_config_catch2_test.cpp Adds tests for rejecting forged/unsigned control artifacts and surfacing legacy unsigned history under writer-auth.
tests/unit/daemon/service_manager_catch2_test.cpp Adds coverage for rejecting insecure/unauthenticated direct P2P config and improperly protected key/trust files.
tests/unit/daemon/peer_registry_catch2_test.cpp Updates tests for operator enrollment semantics and legacy pin migration behavior.
tests/unit/daemon/p2p_protocol_catch2_test.cpp Extends protocol tests to assert operator-enrollment flags flow through trust decisions/handshake.
tests/unit/daemon/p2p_manager_catch2_test.cpp Ensures P2P manager refuses unauthenticated/unrecovered memory-sync services and mismatched identities.
tests/unit/daemon/p2p_delta_catch2_test.cpp Updates cold-bootstrap tests to require explicit operator enrollment (no TOFU-based bootstrap authority).
tests/unit/daemon/components/config_resolver_test.cpp Adjusts config parsing expectations to require writer-auth for direct transport.
src/daemon/p2p/peer_registry.cpp Adds operator_enrollment_generation and gates operator pins on re-enrollment.
src/daemon/p2p/p2p_protocol.cpp Threads pinnedByOperator through trust preflight/commit and handshake results.
src/daemon/p2p/p2p_manager.cpp Requires directP2pReady() (fully recovered + authenticated + identity match) before manager creation.
src/daemon/p2p/p2p_delta.cpp Enforces operator enrollment for cold bootstrap on both send/receive paths.
src/daemon/meson.build Propagates advapi32 dependency on Windows through the daemon library/dep.
src/daemon/components/ServiceManager.cpp Implements owner-only key creation/loading across platforms, authenticated store marker creation, and direct-mode fail-closed gates.
src/daemon/components/ConfigResolver.cpp Disables direct memory_sync when writer-auth requirements are not met.
include/yams/memory_sync/memory_sync.h Introduces signed/verified control artifacts, control scope, and legacy-unsigned-history detection.
include/yams/memory_sync/memory_sync_service.h Exposes readiness checks and propagates legacy-history observation into direct P2P gating.
include/yams/memory_sync/memory_sync_config.h Adds control scope and pluggable key/trust-file readers for permission-checked loading.
include/yams/daemon/p2p/peer_registry.h Updates peer registry API for new enrollment-generation behavior.
include/yams/daemon/p2p/p2p_protocol.h Extends trust/handshake structures to carry pinnedByOperator and adds in-memory enrollment helper.
include/yams/daemon/components/ServiceManager.h Adds test hooks for protected key write/load helpers.
include/yams/daemon/components/AsyncInitOrchestrator.h Adds synchronization to avoid races between start/stop and future installation.
docs/p2p.md Documents authenticated direct setup, manifest format, migration procedure, and enrollment requirement for cold bootstrap.
Review details

Suppressed comments (2)

src/daemon/components/ServiceManager.cpp:383

  • These ACL error messages run for both private keys and trust files, but they currently say "P2P trust file" unconditionally. Using a generic label (e.g., "P2P file") avoids confusing diagnostics when validating the identity/private key.
            if ((!ownerSid && ownerOnlyRead && ace->Mask != 0) ||
                (!ownerSid && (ace->Mask & writable) != 0)) {
                return yams::Error{yams::ErrorCode::Unauthorized,
                                   "P2P trust file ACL grants unsafe external access"};
            }

src/daemon/components/ServiceManager.cpp:456

  • On POSIX, readProtectedP2pFile is reused for both private keys and trust files, but these error messages mix the two (e.g., returning "P2P trust file..." and then "P2P identity key..." in the same code path). This can make failures ambiguous; consider formatting the message based on ownerOnlyRead (private key vs trust file).
        return yams::Error{yams::ErrorCode::Unauthorized,
                           "P2P trust file must be an effective-user-owned regular file without "
                           "unsafe permissions"};
    }
    if (info.st_size <= 0 ||
        static_cast<std::uintmax_t>(info.st_size) > static_cast<std::uintmax_t>(maxBytes)) {
        closeFile();
        return yams::Error{yams::ErrorCode::InvalidArgument,
                           "P2P identity key is empty or oversized"};
  • Files reviewed: 23/23 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +366 to +371
const auto* ace = static_cast<const ACCESS_ALLOWED_ACE*>(rawAce);
auto* sid = const_cast<DWORD*>(&ace->SidStart);
if (!IsValidSid(sid) || GetLengthSid(sid) > header->AceSize - sidOffset) {
return yams::Error{yams::ErrorCode::Unauthorized,
"P2P trust file ACL contains an invalid SID"};
}
@trvon
trvon merged commit 1c30839 into experimental Aug 30, 2026
4 checks passed
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