PRD-01: capability-gated Resolve (resolver authentication) - #55
Merged
Conversation
Replace the same-uid-only Resolve gate with an unforgeable capability so the public proposer socket can no longer self-approve — closing THREAT-MODEL residual-risk #1 and unblocking any external proposer (#48/#50). Mechanism (resolves ADR 0001 PRD-01 open question → socketpair inheritance, app is the parent): the app mints an AF_UNIX socketpair before spawning the daemon, hands the child one end by fd inheritance (DECKARD_RESOLVE_FD), and keeps the other as a ControlChannel. The daemon honours Resolve only on that inherited end (Channel::Control); a Resolve on the public socket is refused with a typed resolve_not_authorized denial. STOP stays reachable on every channel. Each respawn re-mints the pair; the channel fails closed while the daemon is restarting. - deckard-contract: add resolve_not_authorized to the frozen deny vocabulary (no wire/frame change — the channel carries authority, so #31 doesn't apply). - deckard-signerd: Channel enum + gate in Daemon::handle; serve_control + adopt_control_fd (the one daemon-side unsafe, scoped + validated: rejects a non-stream fd, sets close-on-exec); ControlChannel + control_pair in supervise.rs with respawn re-handshake; blocking frame helpers. - deckard-app: route Resolve over the control channel, Execute over the public socket (execute only signs an already-Allowed record). - Tests: new resolver_auth.rs (public-socket Resolve rejected, control-channel accepted, STOP-on-public, red-team second-proposer-cannot-self-approve); migrate existing resolve sites to the control channel. - THREAT-MODEL residual #1 → Mitigated; ADR records the decision. CONTROL_TIMEOUT exceeds the daemon's broadcast lock-hold so normal back-pressure isn't mistaken for a dead channel.
…ness-brief-3dazc3 # Conflicts: # crates/deckard-app/src/signer.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replace the same-uid-only Resolve gate with an unforgeable capability so the
public proposer socket can no longer self-approve — closing THREAT-MODEL
residual-risk #1 and unblocking any external proposer (#48/#50).
Mechanism (resolves ADR 0001 PRD-01 open question → socketpair inheritance,
app is the parent): the app mints an AF_UNIX socketpair before spawning the
daemon, hands the child one end by fd inheritance (DECKARD_RESOLVE_FD), and
keeps the other as a ControlChannel. The daemon honours Resolve only on that
inherited end (Channel::Control); a Resolve on the public socket is refused
with a typed resolve_not_authorized denial. STOP stays reachable on every
channel. Each respawn re-mints the pair; the channel fails closed while the
daemon is restarting.
(no wire/frame change — the channel carries authority, so Wire-contract evolution: Hello capability discovery + the five evolution rules #31 doesn't apply).
adopt_control_fd (the one daemon-side unsafe, scoped + validated: rejects a
non-stream fd, sets close-on-exec); ControlChannel + control_pair in
supervise.rs with respawn re-handshake; blocking frame helpers.
socket (execute only signs an already-Allowed record).
accepted, STOP-on-public, red-team second-proposer-cannot-self-approve);
migrate existing resolve sites to the control channel.
CONTROL_TIMEOUT exceeds the daemon's broadcast lock-hold so normal
back-pressure isn't mistaken for a dead channel.