Skip to content

feat(channels): add tracked bot setup links and signed callbacks - #1705

Merged
ctkm-aelf merged 7 commits into
rollup-2026-09-29-ctkm-1from
nyxid-bot-connection-callback
Sep 30, 2026
Merged

ctkm-aelf merged 7 commits into
rollup-2026-09-29-ctkm-1from
nyxid-bot-connection-callback

Conversation

@ctkm-aelf

@ctkm-aelf ctkm-aelf commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

Squash-merged into rollup-2026-09-29-ctkm-1 as a29056ef8328ae3cfd5bcca51a352e1408163c04. The landed tree exactly matches validated source d08d3d0c; stable patch IDs also match. Included in #1700.

Summary

  • Add tracked, single-use bot setup links so platforms and agents can create a hosted setup URL and receive signed completion, cancellation, or expiry webhooks. POST /api/v1/channel-connect-links and the /connect/bot/{token} page cover manual, managed, and Telegram setup; Agent Keys can create/read/cancel links while completion requires an authorized human.
  • Add optional browser return URLs, direct webhook receivers with a one-time signing secret, and registered OAuth app callbacks using the existing app configuration. Terminal events have a durable snapshot and stable identity, leased delivery, bounded retries, and a separate app quota. Direct and app bot callbacks use public HTTPS destinations with DNS pinning and redirects disabled.
  • Add CLI create/status/cancel commands and matching API, implementation, web, CLI, and agent-playbook documentation. Direct webhook CLI setup requires --webhook-signing-secret-file: the secret is saved to a new file (mode 0600 on Unix), while terminal and JSON output contain only its path and key ID. Existing files and symlinks are rejected before creation; the HTTP API still returns the signing secret once. Rebuild the embedded CLI wizard because its telemetry source closure changed.

Backend head/base coverage builds use CARGO_PROFILE_TEST_DEBUG=line-tables-only to reduce compiler memory demand after repeat runner shutdowns during full-DWARF compilation. The tests, LLVM coverage instrumentation, and 73% backend line threshold remain unchanged; the contributor guide records the recurring-failure response.

Connector compatibility

Existing production service Connector links retain their routes, request/response schemas, token behavior, event names, frontend flows, and webhook delivery behavior. The service-link helper change only exposes the existing app/callback validator within the crate. The existing developer-webhook entry point retains its original HTTP client, timestamp behavior, and quota; bot links explicitly opt into their separate delivery behavior and quota. Existing bot setup entry points retain their signatures and select the new link-aware behavior only when a link is supplied.

The bot-link contract and source map are documented in docs/CHANNEL_BOT_RELAY.md; docs/API.md cross-references the existing service-link API and the new bot-link API. Published web and CLI guides include creation examples, event payloads, signature verification, retries, and callback setup.

Test Plan

  • 19 bot-link backend tests passed against a dedicated MongoDB replica set, covering lifecycle/concurrency, signed delivery, retries, URL validation, authorization, managed setup, Telegram recovery, and terminal-event behavior.
  • 180 backend regression tests passed across existing Connector-link handlers/services, developer webhook dispatch/delivery, bot setup, managed setup, and Telegram flows. This run overlaps one of the bot-link tests above.
  • 92 CLI tests passed across channel-bot commands, existing connect commands, and argument parsing, including secret-free output, file permissions, overwrite/symlink protection, and cleanup after a failed create request. Captured CLI output contains neither test signing secret.
  • Frontend: 92 tests passed across 10 relevant files, followed by 19 tests across the 2 changed UI files after the final UI updates (93 distinct tests). Targeted ESLint and the full production build, including TypeScript and the credential-accept bundle checks, passed.
  • Documentation: 18 tests passed; 43 added links and both API JSON examples validated; documentation publication build passed.
  • Rebuilt the CLI wizard and passed cargo test -p nyxid-cli --test wizard_bundle_freshness.
  • cargo fmt --all -- --check and git diff --check passed. CLI Clippy passed across all targets with -D warnings after the secret-file change.
  • Workflow YAML parsed; an instrumented Rust fixture produced identical coverage totals with full DWARF and line-table debug information.
  • Integrated rollup 012dd86f (feat(chat): open connector and channel setup in reusable overlays #1707). The shared bot-setup conflict preserves overlay stayInPlace behavior and tracked-link result/one-time-secret retention. On combined source 5147c6c4: 134 focused frontend tests, 25 Playwright browser cases, targeted ESLint, and the full production build pass. Existing Connector page, shared content, and hooks have no feature diff against the rollup.
  • Full backend tests, workspace Clippy, all KMS feature builds, backend image inputs, CLI and frontend checks, head backend coverage, and CodeQL passed on f00c203d before the overlay integration. The code-scanning API reports no open alerts for this PR.
  • Synced main ce4414e6 (fix: NyxBot specialists answer again and NyxBot can grant them services (0.37.1) #1703), rebuilt the wizard, and passed its Rust freshness test. Main was also merged into the rollup as 3b80d68f to preserve ancestry. Merging final source into that rollup reproduces the source tree d63cd0f6141262cde6737c89ec9ea3deeec398d7 exactly.
  • Final combined source d08d3d0c: CI, CodeQL and Release passed. All 30 checks succeeded, four release checks intentionally skipped, none failed or remained pending. Backend nextest: 6,860 passed, two skipped; instrumented coverage run: 6,845 passed, two skipped, 87.40% lines against the unchanged 73% threshold. No open code-scanning alerts.

Validation limit: manual onboarding against live external bot providers was not performed.

Checklist

  • Code follows the project's architecture rules.
  • No hardcoded production secrets or credentials.
  • Error messages do not expose internal details.
  • Documentation updated, including implementation references and Connector-link cross-links.

Comment thread cli/src/commands/channel_bot.rs Fixed
@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

📊 Code coverage

Component Lines Threshold Status Δ vs base
Backend (nyxid) 87.40% 73% ✅ 🔻 -0.02
CLI (nyxid-cli) 71.62% 64% ✅ 🔺 +0.10
Frontend (vitest) 70.98% 15% ✅ 🔻 -0.02

Gate: line coverage must stay at or above the threshold. Ratchet plan (W21): Backend → 55%, CLI → 50%, Frontend → 30% by quarter end.

@ctkm-aelf
ctkm-aelf merged commit a29056e into rollup-2026-09-29-ctkm-1 Sep 30, 2026
34 checks passed
ctkm-aelf added a commit that referenced this pull request Sep 30, 2026
* feat(auth): gate signup invitation codes behind default-on feature flag (#1690)

Signup invitation codes are now an operator choice. The requirement is behind
the global, default-on `auth:invitation-code` feature flag, which replaces the
`INVITE_CODE_REQUIRED` environment variable and can be toggled in
Admin > Feature Flags without a restart.

- Enabled (default): email signup requires a valid code; browser social signup
  can redeem one; native social token exchange still rejects first-time signup.
- Disabled: email and first-time social signup succeed without a code.
- Flag is global only; scoped overrides are rejected. The backend resolves it on
  every signup attempt and /api/v1/public/config reports the effective state.
- Invitation-code admin page, API and CLI are retained; the page states when
  codes are not required. Organization invitations are unchanged.
- Rollout: deploy backend before frontend. Deployments with
  INVITE_CODE_REQUIRED=false must disable the flag to keep signup open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(rollup): record 2026-09-29 ctkm-1 rollup

Records #1690 (default-on auth:invitation-code signup gate), its reviewed
head and squash commit, the main sync to e96a507, rollout notes and
verification.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(oauth): redesign consent screen to match connection flows (#1701)

Rework the OAuth consent page into the centered card used by connect links
and channel-bot connections: circular NyxID mark, "Authorize application"
heading with the requesting app highlighted, plain-language permission rows,
and a trust note above Allow/Decline. Client ID, redirect and raw scopes move
under App details. The Low/Medium/High scope badges are removed; they were
client-side labels, not server decisions.

Service access shows each service's catalog description (two lines max) in a
scrollable list with edge fades. Customize toggles a picker that closes via
Done or Save selection. GET /user-services list responses now include
catalog_service_description. Form fields posted to
/oauth/authorize/decision are unchanged.

Adds a dev-only /oauth-consent-preview route with sample data and disabled
decisions.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* test(nyxbot): wait for relayed message instead of a fixed sleep

the_same_question_is_not_worked_on_twice slept 300 ms after a Lark relay
callback that returns 202 before recording the message. The instrumented
coverage build on rollup PR #1700 took longer, so the reply target was not
yet recorded. Poll the home thread (bounded to 10 s) until it is, keeping
the assertions unchanged. Record the follow-up in the rollup notes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(rollup): record #1701 in the 2026-09-29 ctkm-1 rollup

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(auth): allow the code-free registration test 5 s under coverage

The #1690 test 'allows email registration without a code when the flag is
disabled' failed once in the rollup's frontend coverage run: the register
call had not happened within waitFor's default 1 s. Use a 5 s timeout, as
other slow frontend waits do, and record the follow-up in the rollup notes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(billing): keep historical usage priced after price removal (#1702)

Re-authoring a service price deleted the retired Lago code's
billing_rate_cache row, so every historical usage_meter row recorded
under that code lost its only price and GET /billing/usage returned
null costs; the Usage page then showed Unavailable for the service and
for the Spend total. Grants were never the cause.

- Price removal marks the rate row retired_at instead of deleting it;
  new reservations refuse retired rates, historical pricing keeps
  reading them, and a re-synced price clears the mark.
- get_usage prices historical groups by grant-settled derivation, then
  the cached rate, then per-row reservation rates (exact Decimal128
  credits, as exact settlement does), else null with grant credits
  still known.
- The Usage page shows lower bounds (≥) with an unpriced-record count
  instead of Unavailable when only some records are unpriced.
- Docs: glossary precedence, admin usage limitation, rollout note.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* docs(rollup): record #1702 in the 2026-09-29 ctkm-1 rollup

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* ci: build backend test binaries with line-table debuginfo

Backend Test and Backend Billing Smoke died five times out of seven
attempts on the 2026-09-29 rollup head with exit 143 ("The runner has
received a shutdown signal") four to five minutes into compiling the
nyxid test binary, before any test ran. The full-DWARF test binary
exhausts the hosted runner's memory during codegen and linking. Both
jobs now set CARGO_PROFILE_TEST_DEBUG=line-tables-only, which keeps
panic locations and backtraces and changes nothing for local builds or
the coverage jobs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* feat(chat): open connector and channel setup in reusable overlays (#1707)

* feat(chat): open connector and channel setup in reusable overlays

* fix(chat): harden setup popup lifecycle and restore link focus

* feat(channels): add tracked bot setup links and signed callbacks (#1705)

* feat(channels): add tracked bot setup links and signed callbacks

* fix(channels): preserve bot creation calls in production builds

* fix(cli): save bot webhook signing secrets to private files

* ci: bound backend coverage debug information

* docs: record bot-link callbacks and rollup verification

* fix(oauth): make Google Workspace grants optional (#1706)

* fix(oauth): make Google Workspace grants optional

Accept partial Google consent, remove seeded required-scope metadata across Workspace products, and show last reported grants in service details.

* test(oauth): match optional Workspace seed wording

* feat(oauth): show grants for every Google Workspace product

* test(oauth): retain prior scope evidence when response omits scopes

* ci: bound backend coverage debug info for hosted runners

* docs: record Google OAuth grant fix in rollup

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
ctkm-aelf added a commit that referenced this pull request Sep 30, 2026
* rollup: 2026-09-29 ctkm-1 integration into main (#1700)

* feat(auth): gate signup invitation codes behind default-on feature flag (#1690)

Signup invitation codes are now an operator choice. The requirement is behind
the global, default-on `auth:invitation-code` feature flag, which replaces the
`INVITE_CODE_REQUIRED` environment variable and can be toggled in
Admin > Feature Flags without a restart.

- Enabled (default): email signup requires a valid code; browser social signup
  can redeem one; native social token exchange still rejects first-time signup.
- Disabled: email and first-time social signup succeed without a code.
- Flag is global only; scoped overrides are rejected. The backend resolves it on
  every signup attempt and /api/v1/public/config reports the effective state.
- Invitation-code admin page, API and CLI are retained; the page states when
  codes are not required. Organization invitations are unchanged.
- Rollout: deploy backend before frontend. Deployments with
  INVITE_CODE_REQUIRED=false must disable the flag to keep signup open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(rollup): record 2026-09-29 ctkm-1 rollup

Records #1690 (default-on auth:invitation-code signup gate), its reviewed
head and squash commit, the main sync to e96a507, rollout notes and
verification.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(oauth): redesign consent screen to match connection flows (#1701)

Rework the OAuth consent page into the centered card used by connect links
and channel-bot connections: circular NyxID mark, "Authorize application"
heading with the requesting app highlighted, plain-language permission rows,
and a trust note above Allow/Decline. Client ID, redirect and raw scopes move
under App details. The Low/Medium/High scope badges are removed; they were
client-side labels, not server decisions.

Service access shows each service's catalog description (two lines max) in a
scrollable list with edge fades. Customize toggles a picker that closes via
Done or Save selection. GET /user-services list responses now include
catalog_service_description. Form fields posted to
/oauth/authorize/decision are unchanged.

Adds a dev-only /oauth-consent-preview route with sample data and disabled
decisions.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* test(nyxbot): wait for relayed message instead of a fixed sleep

the_same_question_is_not_worked_on_twice slept 300 ms after a Lark relay
callback that returns 202 before recording the message. The instrumented
coverage build on rollup PR #1700 took longer, so the reply target was not
yet recorded. Poll the home thread (bounded to 10 s) until it is, keeping
the assertions unchanged. Record the follow-up in the rollup notes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(rollup): record #1701 in the 2026-09-29 ctkm-1 rollup

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(auth): allow the code-free registration test 5 s under coverage

The #1690 test 'allows email registration without a code when the flag is
disabled' failed once in the rollup's frontend coverage run: the register
call had not happened within waitFor's default 1 s. Use a 5 s timeout, as
other slow frontend waits do, and record the follow-up in the rollup notes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(billing): keep historical usage priced after price removal (#1702)

Re-authoring a service price deleted the retired Lago code's
billing_rate_cache row, so every historical usage_meter row recorded
under that code lost its only price and GET /billing/usage returned
null costs; the Usage page then showed Unavailable for the service and
for the Spend total. Grants were never the cause.

- Price removal marks the rate row retired_at instead of deleting it;
  new reservations refuse retired rates, historical pricing keeps
  reading them, and a re-synced price clears the mark.
- get_usage prices historical groups by grant-settled derivation, then
  the cached rate, then per-row reservation rates (exact Decimal128
  credits, as exact settlement does), else null with grant credits
  still known.
- The Usage page shows lower bounds (≥) with an unpriced-record count
  instead of Unavailable when only some records are unpriced.
- Docs: glossary precedence, admin usage limitation, rollout note.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* docs(rollup): record #1702 in the 2026-09-29 ctkm-1 rollup

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* ci: build backend test binaries with line-table debuginfo

Backend Test and Backend Billing Smoke died five times out of seven
attempts on the 2026-09-29 rollup head with exit 143 ("The runner has
received a shutdown signal") four to five minutes into compiling the
nyxid test binary, before any test ran. The full-DWARF test binary
exhausts the hosted runner's memory during codegen and linking. Both
jobs now set CARGO_PROFILE_TEST_DEBUG=line-tables-only, which keeps
panic locations and backtraces and changes nothing for local builds or
the coverage jobs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* feat(chat): open connector and channel setup in reusable overlays (#1707)

* feat(chat): open connector and channel setup in reusable overlays

* fix(chat): harden setup popup lifecycle and restore link focus

* feat(channels): add tracked bot setup links and signed callbacks (#1705)

* feat(channels): add tracked bot setup links and signed callbacks

* fix(channels): preserve bot creation calls in production builds

* fix(cli): save bot webhook signing secrets to private files

* ci: bound backend coverage debug information

* docs: record bot-link callbacks and rollup verification

* fix(oauth): make Google Workspace grants optional (#1706)

* fix(oauth): make Google Workspace grants optional

Accept partial Google consent, remove seeded required-scope metadata across Workspace products, and show last reported grants in service details.

* test(oauth): match optional Workspace seed wording

* feat(oauth): show grants for every Google Workspace product

* test(oauth): retain prior scope evidence when response omits scopes

* ci: bound backend coverage debug info for hosted runners

* docs: record Google OAuth grant fix in rollup

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* feat: NyxBot guests use specialists' services at owner-set levels; Telegram bot creators are owners; Lark groups answer only bot mentions (0.38.0) (#1712)

* fix: a Telegram bot created through NyxID knows its owner: the creating account is verified on link or first message, and Start greets instead of refusing

* fix: only a creator bound by the owner's own setup challenge is trusted, in private chats; link-time grants are audited and NyxBot keeps the verify link as a fallback

* chore: bump version to 0.37.2

* fix: guests use a specialist's services except deleting; Lark groups count only mentions of the bot itself

* fix: guests are refused calls that look like deleting by what they send, and never raise approvals; Lark bot ids cached per app

* fix: guest delete checks cover GETs, body method overrides, unparsable JSON and code fields; guests never run on the owner's approval grants

* feat: owners set what guests may do with each of a specialist's services (read, use, all), from the agent page or by asking NyxBot; destructive operations come from spec markers, not word lists

* test: agent fixtures carry guest_access

* fix: guest access judges every requested method, keeps levels beside grants for rolling deploys, honours read-only specs and catalog markers on mounted specs; Drive trash and content replace are destructive

* fix: grants form builds its request without an unused binding

* fix: guest calls never carry method overrides; Calendar event and Bitable record updates are destructive; a re-granted service starts at the default guest level

* fix: guests' default use is reading, creating and acting (no PUT/PATCH/DELETE); Aevatar's destructive markers stay unchanged and NyxID marks POST edits itself; only stored catalog contracts widen reads; override keys read as PHP reads them

* fix: NyxID's per-operation x-nyxid-changes-existing (true for POST edits, false for PUT actions); override checks follow the body's content type; hosted overlays mounted by URL are catalog contracts; all-level grants say they include deleting

* fix: only a catalog contract may say an operation only acts; method fields count when they name another changing verb, in JSON bodies too; ; separates fields; hosted overlays share one check

* fix: override checks skip empty, text and binary bodies; hosted overlay lookups share NyxID's compiled copy without cloning

* chore: bump version to 0.38.0

---------

Co-authored-by: chrono-kw <chrono-kw@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: chronoai-kai <kaiweichronoai@gmail.com>
Co-authored-by: chrono-kw <chrono-kw@users.noreply.github.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