Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ product overview.

## Unreleased

- Add an opt-in admin sign-in alternative using the admin password and a private, expiring Slack
verification code for existing administrators, including sensitive-action reauthentication.

- Version login-page scripts with their HTML so cached pre-upgrade code cannot reference removed fields.

- Use the existing Users list and admin role for dashboard passkeys and invitations. Existing
Expand Down
14 changes: 13 additions & 1 deletion FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@

Slack’s native running/Stop label shows the resolved model and explicitly configured effort, plus compact live phases (Thinking, Using tools, Writing, Waiting, Queued, Coordinating agents). Engine fallback replaces both model and effort; unset effort is omitted rather than guessed.

## Optional password and Slack code sign-in

- Settings → Admin access can enable an alternative to passkeys: the configured admin password
followed by an eight-digit code delivered privately to an existing Slack administrator.
The option is off by default; password alone never grants dashboard access. Existing passkeys
remain available. A recently verified, identity-linked initial setup can enable this option
without first registering a passkey.
- Codes expire after five minutes, are bound to the requesting browser, accept at most five
guesses and are single-use. Delivery failures, role revocation, password changes and disabling
the option invalidate pending authority. Sensitive actions can repeat password and Slack
verification. Sessions retain the normal idle/absolute expiry and current-role checks.

## User-linked dashboard passkeys

- The existing **Users** directory and its admin role govern dashboard access. Each credential
Expand All @@ -27,7 +39,7 @@ Slack’s native running/Stop label shows the resolved model and explicitly conf
session, with five attempts maximum. Matching names never assign ownership. Unlinked accounts
can complete this verification but cannot use ordinary admin APIs once known admins exist.
Initial password setup follows the same proof before enrollment; fresh installs without known
admins retain temporary operator setup. First enrollment permanently retires password login.
admins retain temporary operator setup. First enrollment retires the password-only bootstrap path; the optional password plus Slack code flow requires explicit enablement.
- Host operators recover a known admin with `npm run admin:recover -- --user <user-id>`.
Recovery revokes its keys/sessions and prints a short-lived registration-only link; it never
restores password login. Private keys remain with authenticators; SQLite stores public keys
Expand Down
33 changes: 33 additions & 0 deletions TEST-PLAN.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,38 @@
# ChannelGate — Test Plan

## Optional password and Slack code authentication (2026-10-28)

- Automated scratch HTTP fixture: `test/admin-password-slack.test.js` and
`test/admin-password-slack-race.test.js`. Use fake private delivery,
disposable passwords and existing Slack Admin/non-admin users. Verify disabled-by-default,
password-only denial, correct browser-bound code login after passkey enrollment, replay/expiry/
guess limits, private delivery failure, role and credential/settings revocation. Never send
fixture codes to deployed users.
- Browser fixture: `test/admin-password-slack-browser.test.js` exercises Settings → Admin access enablement, alternate sign-in and sensitive
reauthentication and sign-out on disable without a WebAuthn device. Chromium verification:
all 15 combined passkey/initial-setup/alternate-login assertions passed, zero skips; the
extended disable/sign-out browser fixture also passed. Private delivery is simulated.
- Live acceptance pending, engine-independent: this HTTP/browser authentication path never calls
either engine. On a disposable gateway with an existing Slack administrator and working bot DM
delivery, enable the checkbox using recent admin verification, sign out, enter the password
and own Slack member ID, then enter the privately received code. Pass only when the dashboard
opens as that exact existing user with no passkey prompt. Repeat a sensitive action with code
reauthentication. Confirm a wrong/replayed code and a non-admin are refused; disabling the
checkbox removes alternate login and invalidates its sessions. Check from the RustDesk browser
through a localhost SSH tunnel. Record screenshots/HTTP evidence without codes/passwords.
- Local regression: `npm run test:coverage -- --test-concurrency=4` passed with 4,120
passed / 38 opt-in skipped / zero failed; coverage 93.91% lines, 86.55% branches, 90.27%
functions. An initial unconstrained run hit the container process limit and was terminated;
it is not counted as a passing run. Security-coverage, secret scan and production dependency
audit at the high severity threshold passed (three existing moderate advisories remain).
- CI fixture isolation: `test/web-security.test.js` now initializes scratch paths before
importing security helpers. Its isolated 15-test run under a fresh HOME created no runtime
files there; this fixes the existing real-home leak without relaxing the guard.
- Gate maintenance: linked-workspace authorization cases in `test/authorization.test.js`
restore the existing 95% branch floor (measured 96.81%); no production policy or floor changed.
- Private QA registry update pending: the requesting user's personal Airtable connection is not
connected in this run. Do not treat the missing registry or unexecuted live case as passed.

## Linked working-folder setup acceptance

Re-verification (2026-10-26): regression coverage now checks source `excludedUsers` and restricted-channel policy through linked DMs, explicit-unlink/source-departure section-skill retention, DM template preservation, established promoted-owner selection, and immediate follower SSH disconnection after source revocation (20-minute timer disabled as a fallback). `test/skills-workspace-sync.test.js` and `test/ssh-broker.test.js` cover these gaps. Updated full suite: 4,117 passed, 37 skipped, zero failures; Chromium linked-folder fixture passed. Actual Claude and Codex CLI smoke checks read a source-scoped skill from a generated linked workspace and returned its unique marker. Codex used the gateway's Worker/container mode; an initial nested read-only sandbox attempt failed with a bubblewrap capability error. These are CLI smoke checks, not a Slack end-to-end acceptance pass.
Expand Down
18 changes: 9 additions & 9 deletions documentation/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion documentation/src/content/docs/controls.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { controlPages } from '../../data/control-reference.mjs';

Ask the agent for an outcome in ordinary language. It can use the controls below when its current permissions, platform, and connections allow them. These reference pages explain the exact tool names, inputs, results, and restrictions for people building integrations or checking how an action works.

The gateway defines **110 controls**. The separate [skills-library service](/docs/controls/skills-library) defines **10 more**. A run receives an authorized subset; a tool appearing here does not mean it is enabled in every conversation. The library endpoint uses its own access tokens.
The gateway defines **121 controls**. The separate [skills-library service](/docs/controls/skills-library) defines **10 more**. A run receives an authorized subset; a tool appearing here does not mean it is enabled in every conversation. The library endpoint uses its own access tokens.

For practical workflows, browse the [feature directory](/docs/features). For deployment and conversation defaults, open [configuration](/docs/configuration). The [functionality map](/docs/functionality) connects the broader product areas to their guides.

Expand Down
52 changes: 52 additions & 0 deletions documentation/src/content/docs/controls/accounts-and-secrets.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,55 @@ An email account is a mailbox's IMAP and SMTP settings entered once on one form
## Related guides

[Accounts](/docs/features/connected-accounts), [environment configuration](/docs/configuration/environment-variables), [secrets](/docs/features/secrets), [Composio SDK](/docs/features/composio-sdk), and [Toolbox/Make](/docs/features/toolbox-and-make).

## list_email_accounts

**Inputs:** None.

**Scope and result:** Lists this conversation's connected email accounts and the verified requester's personal accounts, with address, scope, server endpoints, and sending availability. Never returns passwords. Connect accounts through Settings → Email accounts (MCP page for the conversation, You page for personal accounts), not through chat. Personal mail read in a shared conversation appears in that shared thread. Untrusted API author attribution does not grant personal accounts.

The email controls below accept optional `account`: a connected account name, `personal/<name>`, `channel/<name>`, or email address. Omit it only when exactly one account is available. Optional `mailbox` defaults to `INBOX`; message `uid` values are scoped to that mailbox.

## email_mailboxes

**Optional:** `account`.

**Scope and result:** Lists selectable folders and their special roles (Inbox, Sent, Drafts, Archive, Trash, Junk) for the selected connected account.

## email_search

**Optional:** `account`, `mailbox`, `query`, `from`, `to`, `subject`, `since`, `before`, `unseen`, `flagged`, `limit` (integer 1–50; default 20).

**Scope and result:** Lists matching messages newest first, including UID, date, sender, subject, unread/flag marks, and attachment count. Filters combine with AND; no filters lists newest messages. `since` and `before` are ISO dates. Results include the total match count. Use the returned UID with the message controls.

## email_read

**Required:** `uid` (positive integer). **Optional:** `account`, `mailbox`, `max_chars` (integer 500–100000; default 20000).

**Scope and result:** Reads headers, text body (plain text preferred, HTML converted), and attachment part identifiers. Reports body truncation explicitly.

## email_download_attachment

**Required:** `uid` (positive integer), `part` (attachment part ID from `email_read`). **Optional:** `account`, `mailbox`.

**Scope and result:** Downloads an attachment into this conversation's workspace under `uploads/email/<account>/` and returns its local path. Maximum attachment size is 50 MB. The path can be read locally or shared using the conversation's file controls.

## email_move

**Required:** `uid` (positive integer), `to` (destination mailbox path). **Optional:** `account`, `mailbox`.

**Scope and result:** Moves a message into another folder, such as Archive or Trash. Returns the source/destination and new UID when supplied by the server. List valid destinations with `email_mailboxes`.

## email_set_flag

**Required:** `uid` (positive integer), `flag` (`seen`, `unseen`, `flagged`, `unflagged`, or `answered`). **Optional:** `account`, `mailbox`.

**Scope and result:** Changes the selected message's read, flagged, or answered state and returns an acknowledgement.

## email_send

**Required:** `to` (nonempty array of recipient addresses), `subject` (string), `text` (plain-text body). **Optional:** `account`, `cc`, `bcc` (address arrays), `html` (HTML alternative), `attachments` (workspace-relative file paths), `in_reply_to` (original Message-ID), `reply_to` (address).

**Scope and result:** Sends from the selected connected account's address and reports accepted/rejected recipients, Message-ID, and whether a Sent copy was saved. `in_reply_to` sets both In-Reply-To and References. Attachments are confined to the conversation workspace, at most ten files and 25 MB total.

**Restrictions:** Sending must be enabled on the account. Confirm recipients and content unless the user already supplied them. Personal accounts send as that person. Mail credentials remain in the daemon, outside agent containers.
26 changes: 26 additions & 0 deletions documentation/src/content/docs/controls/slack.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,29 @@ These tools use the gateway’s Slack bot token and trusted current conversation
**Scope and result:** Asks Slack for a file descriptor, proves sharing in the current conversation, and downloads into this thread’s uploads folder. Returns a local path, MIME type when available, and size. A existing confined download is reused.

**Limits:** At most 500 MiB per attachment. The bot must have file access and Slack must report the file shared here. It returns no private URL/token. Download refusals and unavailable metadata do not count as content inspection.

## open_code_channel

**Required:** `task` (string, 3–2000 characters). **Optional:** `name` (string, up to 80 characters), `mode` (`auto`, `worktree`, or `in_place`; default `auto`), `private` (boolean; default inherits the origin channel).

**Scope and result:** Requests a dedicated Slack code channel for an explicitly requested task. A human must approve the card before creation. The new channel inherits the origin workspace, access policy, secrets, and skills. `auto` creates a task branch and worktree when the workspace is a repository; otherwise it uses the channel's files. Returns the channel and workspace/branch outcome; the daemon starts the first turn when available.

**Restrictions:** Requires an originating Slack message in a configured channel. Not available from a DM, another code channel, or a scheduled/background run without an originating message. Call only when the requester explicitly asks for a code channel.

## update_code_channel

**Optional:** `title` (string, up to 200 characters), `refresh_diff` (boolean), `context_items` (up to five objects with required `key` and `label`, optional `icon` and `url`), `view` (object).

The `view` object requires `type` (`html`, `block_kit`, `canvas`, or `pull_request`) and accepts `key`, `name`, `content` (complete self-contained HTML), `blocks` (up to 50 Block Kit blocks), `canvas_id`, and `pr_url` as appropriate for the type. Reusing a stable `key` replaces that artifact tab.

**Scope and result:** Updates the current code channel's title, context bar, artifact tab, or Code diff independently. Context items merge with defaults; the complete context bar holds up to five items. Returns an outcome for each requested change. The Code diff normally refreshes after each turn; `refresh_diff:true` forces a refresh.

**Restrictions:** Only available inside an active Slack code channel. HTML previews render sandboxed. An archived code channel cannot be updated.

## archive_code_channel

**Optional:** `summary` (string, up to 3900 characters).

**Scope and result:** Requests archival of the current Slack code channel after its task is done and the requester agrees. A human must approve the card. Posts the wrap-up, shares it into the origin thread, and archives the channel. History and artifact tabs remain readable and searchable; the task branch remains on disk.

**Restrictions:** Only available inside a Slack code channel. Repeated archival reports that the channel is already archived.
12 changes: 10 additions & 2 deletions documentation/src/content/docs/features/admin-dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,18 @@ The admin website runs as part of your ChannelGate installation. It manages the

## Sign in

By default, the daemon serves the interface on `http://localhost:4747`. An operator can change the port or put a trusted tunnel/reverse proxy in front of it. On first boot without a configured password, ChannelGate generates an admin password once and stores its hash. Use the password supplied by the installation's operator.
By default, the daemon serves the interface on `http://localhost:4747`. An operator can change the port or put a trusted tunnel/reverse proxy in front of it. On first boot without a configured password, ChannelGate generates an admin password once and stores its hash. Use the password supplied by the installation's operator for initial setup, then verify and link your existing gateway administrator user. Normal sign-in uses a passkey, with optional password and Slack code sign-in as described below.

A public deployment should set **Settings → Connection → Public URL** to its reachable HTTPS origin. This also gives approval links a reachable address. Setting Public URL does not create a tunnel or open a firewall.

## Optional password and Slack code sign-in

A linked Slack administrator can enable **Settings → Admin access → Password and Slack code** after a fresh sign-in. It is off by default. Keep the current shared admin password or set a new one of at least 12 characters. During initial setup, verify and link the administrator first, then use this option to complete setup without creating a passkey.

On the sign-in page, choose **Sign in with password and Slack code**, enter the shared admin password and your Slack user ID, then enter the eight-digit code delivered privately by the gateway bot. Codes expire after five minutes, work only in the requesting browser, and allow at most five incorrect guesses. This option requires a current gateway Slack administrator and working private Slack delivery; approved members alone cannot use it. Passkeys remain available.

Changing the shared password or disabling this option revokes existing password-and-code sessions. Removing the user's administrator role also revokes access. Disabling it while signed in this way signs out that browser; retain another usable sign-in method before disabling it.

## Choose the right page

- **[Overview](/docs/features/usage-and-costs):** run, token, cost, model, user, conversation, and skill summaries.
Expand All @@ -31,7 +39,7 @@ Settings display inherited and explicit values where supported. Blank password/t

Platform connections can be connected or reconnected without restarting the whole daemon. Runtime/image changes may affect future container starts; inspect the resulting status and verify a real task after a change.

Chat admins and the password-authenticated web session are distinct ways of authorizing control actions. Being an approved chat user does not itself provide the admin website password.
Dashboard sessions are linked to an existing gateway administrator user. Being an approved chat user alone does not grant dashboard access. The shared password alone cannot grant a full dashboard session.

## Related guides

Expand Down
Loading
Loading