feat: Approval Paths — pluggable manual-approval delivery (OneCLI SDK + ntfy push) - #395
Open
nbmorgan wants to merge 5 commits into
Open
feat: Approval Paths — pluggable manual-approval delivery (OneCLI SDK + ntfy push)#395nbmorgan wants to merge 5 commits into
nbmorgan wants to merge 5 commits into
Conversation
…SDK + ntfy push)
Add an Approval Paths settings section with independently-toggleable delivery
channels for manual-approval decisions:
- OneCLI SDK long-poll (existing path, now gated by an explicit, default-on flag)
- ntfy push with Approve/Deny action buttons → a token-guarded gateway callback
Both channels resolve the same in-memory PendingApproval store, so any
combination can be enabled. New project-scoped ApprovalPath model (additive
migration), per-channel hold timeout (max across enabled channels), and an
encrypted credentials blob with merge-on-save so multiple secret fields don't
stomp each other.
Gateway (OSS modules): db reads + decrypt, notify.rs (ntfy publish + status
note + resolved-decision memory), forward.rs wiring, and callback routes
POST /v1/approvals/{id}/approve|deny that are idempotent and conflict-aware
(same decision -> 200, opposite -> 410, timed-out/unknown -> 408). Variable
expansion ({agentId},{agentName},{method},{host},{path}) in Tags/Priority.
Configurable resolved-decision window (APPROVAL_RESOLVED_TTL_SECS).
Dashboard: Settings -> Approval Paths with per-channel cards, a connection
test that exercises publish -> device -> callback without an agent, a recent-
events debug log, password reveal gated by ONECLI_ALLOW_SECRET_REVEAL, and a
default-on "Report Selection to Topic" confirmation note (security signal +
iOS feedback for silent action buttons).
Also routes /settings via next.config redirects() instead of an in-render
redirect() to avoid the React onecli#310 AppRouter crash on soft-navigation, so the
new settings page is reachable.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
# Conflicts: # apps/gateway/src/gateway/forward.rs
… ntfy channel Align the ntfy push channel with upstream onecli#393's approval summaries + bell: - ntfy notification now renders the gateway's structured ApprovalSummary (the same "Send email · To: … · Subject: …" the dashboard bell shows) as the title + body, falling back to the legacy body preview then a generic line. A footer always names the agent + target. - The "Report Selection to Topic" confirmation note now fires on ANY resolution via a shared helper — not just the ntfy tap but also a dashboard/SDK decision (labelled "via dashboard"), so ntfy subscribers learn when someone ELSE approved. Carries the decision + original request name/time. - Settings copy: the OneCLI SDK card now names the dashboard approvals bell as its surface. Keeps the Approval Paths channel framework as the home for delivery channels (bell = onecli channel, ntfy = push channel). A generalized notifier registry is left for upstream to decide. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Contributor
Author
|
Synced this branch with the latest
The branch is MERGEABLE with no conflicts against current |
# Conflicts: # apps/gateway/src/db.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.
Closes #394.
What
Adds Approval Paths — a Settings section with independently-toggleable delivery channels for
manual_approvaldecisions:Both channels resolve the same in-memory
PendingApprovalstore, so any combination can be enabled.Gateway (OSS modules)
dbreads + decrypt for the new config,notify.rs(ntfy publish + confirmation note + short-TTL resolved-decision memory),forward.rswiring, and callback routesPOST /v1/approvals/{id}/approve|deny.200, opposite →410, timed-out/unknown →408(mobile push clients retry and users double-tap).{agentId},{agentName},{method},{host},{path}) in ntfy Tags/Priority.APPROVAL_RESOLVED_TTL_SECS.Data model
New project-scoped
ApprovalPathmodel. The migration is purely additive (oneCREATE TABLE+ FKs) — backward-compatible / rollback-safe. Multiple secret fields are stored in one encrypted blob with merge-on-save so re-entering one secret doesn't wipe the others.Dashboard
Settings → Approval Paths with per-channel cards, a connection test (exercises publish → device → callback without an agent), a recent-events debug log, password reveal gated by
ONECLI_ALLOW_SECRET_REVEAL, and a default-on Report Selection to Topic confirmation note.Incidental fix (bundled, happy to split)
/settingsis routed vianext.configredirects()instead of an in-render serverredirect(), which avoids a React #310 AppRouter crash on client soft-navigation and is required for the new settings page to be reachable.Testing
cargo clippy -- -D warningsclean; gateway unit tests pass (incl. resolved-decision logic).pnpm check(lint + types + format) green.Notes for reviewers
This came out of a self-hosted deployment, so some defaults/copy lean toward that use case — glad to adjust naming, split the
next.configfix into its own PR, or scope the ntfy specifics differently. Opening this to make the discussion concrete per #394.