Skip to content

Commit a0fd058

Browse files
committed
bridge: remove direct slack fallback in broker mode
1 parent 27cc89c commit a0fd058

7 files changed

Lines changed: 124 additions & 218 deletions

File tree

CONFIGURATION.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ The agent also uses an SSH key (`~/.ssh/id_ed25519`) for git push. Setup generat
3737

3838
| Variable | Description | How to get it |
3939
|----------|-------------|---------------|
40-
| `SLACK_BOT_TOKEN` | Slack bot OAuth token (required for direct Socket Mode, optional in broker mode) | Create a Slack app at [api.slack.com/apps](https://api.slack.com/apps). Under **OAuth & Permissions**, add bot scopes: `app_mentions:read`, `chat:write`, `channels:history`, `channels:read`, `reactions:write`, `im:history`, `im:read`, `im:write`. Install the app to your workspace and copy the **Bot User OAuth Token**. |
41-
| `SLACK_APP_TOKEN` | Slack app-level token (required for Socket Mode, optional in broker mode) | In your Slack app settings → **Basic Information****App-Level Tokens**, create a token with `connections:write` scope. |
40+
| `SLACK_BOT_TOKEN` | Slack bot OAuth token (required for direct Socket Mode; ignored by broker pull mode) | Create a Slack app at [api.slack.com/apps](https://api.slack.com/apps). Under **OAuth & Permissions**, add bot scopes: `app_mentions:read`, `chat:write`, `channels:history`, `channels:read`, `reactions:write`, `im:history`, `im:read`, `im:write`. Install the app to your workspace and copy the **Bot User OAuth Token**. |
41+
| `SLACK_APP_TOKEN` | Slack app-level token (required for Socket Mode; not used by broker pull mode) | In your Slack app settings → **Basic Information****App-Level Tokens**, create a token with `connections:write` scope. |
4242
| `SLACK_ALLOWED_USERS` | Comma-separated Slack user IDs | **Optional** — if not set, all workspace members can interact. Find your Slack user ID: click your profile → "..." → "Copy member ID". Example: `U01ABCDEF,U02GHIJKL` |
4343

4444
If you're using Slack broker mode (`SLACK_BROKER_*` vars), the runtime uses broker pull delivery and does not require Socket Mode callbacks.
@@ -104,9 +104,9 @@ Set by `sudo baudbot broker register` when using brokered Slack OAuth flow.
104104
| `SLACK_BROKER_SERVER_SIGNING_PUBLIC_KEY` | Server Ed25519 public signing key (base64) |
105105
| `SLACK_BROKER_PUBLIC_KEY` | Broker X25519 public key (base64) |
106106
| `SLACK_BROKER_SIGNING_PUBLIC_KEY` | Broker Ed25519 public signing key (base64) |
107-
| `SLACK_BROKER_ACCESS_TOKEN` | Optional broker-issued bearer token for broker API auth (used when broker enforces agent tokens) |
108-
| `SLACK_BROKER_ACCESS_TOKEN_EXPIRES_AT` | Optional ISO timestamp for broker token expiry |
109-
| `SLACK_BROKER_ACCESS_TOKEN_SCOPES` | Optional comma-separated broker token scopes |
107+
| `SLACK_BROKER_ACCESS_TOKEN` | Broker-issued bearer token for broker API auth (required for broker pull mode runtime) |
108+
| `SLACK_BROKER_ACCESS_TOKEN_EXPIRES_AT` | ISO timestamp for broker token expiry (recommended; runtime exits if expired) |
109+
| `SLACK_BROKER_ACCESS_TOKEN_SCOPES` | Comma-separated broker token scopes |
110110
| `SLACK_BROKER_POLL_INTERVAL_MS` | Inbox poll interval in milliseconds (default: `3000`) |
111111
| `SLACK_BROKER_MAX_MESSAGES` | Max leased messages per poll request (default: `10`) |
112112
| `SLACK_BROKER_WAIT_SECONDS` | Long-poll wait window for `/api/inbox/pull` (default: `20`, set `0` for immediate short-poll, max `25`) |

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ sudo baudbot broker register \
9999
```
100100

101101
Broker pull mode uses long-polling by default (`SLACK_BROKER_WAIT_SECONDS=20`, max `25`; set `0` for immediate short-poll behavior).
102-
When broker agent-token auth is enabled server-side, `baudbot broker register` stores broker token fields in env and broker-mode outbound requests include `Authorization: Bearer ...` automatically.
102+
`baudbot broker register` stores broker token fields in env and broker-mode requests include `Authorization: Bearer ...` automatically.
103+
Broker pull mode no longer uses direct Slack Web API fallback and does not require storing `SLACK_BOT_TOKEN`.
103104

104105
Need to rotate/update a key later?
105106

bin/broker-register.mjs

Lines changed: 2 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -225,48 +225,6 @@ export function mapRegisterError(status, errorText) {
225225
return `registration failed (${status}) — ${text}`;
226226
}
227227

228-
let cachedSodium = null;
229-
230-
async function getSodium() {
231-
if (cachedSodium) return cachedSodium;
232-
233-
try {
234-
const mod = await import("libsodium-wrappers-sumo");
235-
cachedSodium = mod.default ?? mod;
236-
await cachedSodium.ready;
237-
return cachedSodium;
238-
} catch {
239-
return null;
240-
}
241-
}
242-
243-
/**
244-
* Decrypt a sealed box encrypted with the server's public key.
245-
* Returns null if libsodium is unavailable.
246-
*/
247-
async function decryptSealedBox(encryptedBase64, serverPrivateKeyBase64, serverPublicKeyBase64) {
248-
const sodium = await getSodium();
249-
if (!sodium) {
250-
return null;
251-
}
252-
253-
const ciphertext = Buffer.from(encryptedBase64, "base64");
254-
const serverPrivateKey = Buffer.from(serverPrivateKeyBase64, "base64");
255-
const serverPublicKey = Buffer.from(serverPublicKeyBase64, "base64");
256-
257-
const plaintext = sodium.crypto_box_seal_open(
258-
ciphertext,
259-
serverPublicKey,
260-
serverPrivateKey,
261-
);
262-
263-
if (!plaintext) {
264-
throw new Error("Failed to decrypt sealed box - invalid keys or corrupted data");
265-
}
266-
267-
return new TextDecoder().decode(plaintext);
268-
}
269-
270228
export async function registerWithBroker({
271229
brokerUrl,
272230
workspaceId,
@@ -327,24 +285,8 @@ export async function registerWithBroker({
327285
throw new Error("broker signing pubkey mismatch between /api/broker-pubkey and /api/register");
328286
}
329287

330-
let decryptedBotToken = null;
331288
if (registerResponseBody?.encrypted_bot_token) {
332-
logger("Decrypting bot token from broker response...");
333-
try {
334-
decryptedBotToken = await decryptSealedBox(
335-
registerResponseBody.encrypted_bot_token,
336-
serverKeys.server_private_key,
337-
serverKeys.server_pubkey,
338-
);
339-
340-
if (decryptedBotToken) {
341-
logger("✅ Bot token decrypted successfully");
342-
} else {
343-
logger("⚠️ libsodium-wrappers-sumo unavailable; skipping bot token decryption");
344-
}
345-
} catch (err) {
346-
logger(`⚠️ Failed to decrypt bot token, continuing with broker mode only: ${err instanceof Error ? err.message : String(err)}`);
347-
}
289+
logger("ℹ️ broker returned encrypted_bot_token, but runtime no longer stores direct Slack bot tokens in broker mode");
348290
}
349291

350292
return {
@@ -355,7 +297,6 @@ export async function registerWithBroker({
355297
broker_access_token_scopes: Array.isArray(registerResponseBody?.broker_access_token_scopes)
356298
? registerResponseBody.broker_access_token_scopes.filter((scope) => typeof scope === "string")
357299
: undefined,
358-
decrypted_bot_token: decryptedBotToken,
359300
request_payload: registerRequestBody,
360301
};
361302
}
@@ -588,11 +529,6 @@ export async function runRegistration({
588529
updates.SLACK_BROKER_ACCESS_TOKEN_SCOPES = registration.broker_access_token_scopes.join(",");
589530
}
590531

591-
// Add the decrypted bot token if available
592-
if (registration.decrypted_bot_token) {
593-
updates.SLACK_BOT_TOKEN = registration.decrypted_bot_token;
594-
}
595-
596532
return {
597533
updates,
598534
};
@@ -657,11 +593,7 @@ export async function main(argv = process.argv.slice(2)) {
657593
for (const target of input.configTargets) {
658594
console.log(` - ${target.path}`);
659595
}
660-
661-
if (updates.SLACK_BOT_TOKEN) {
662-
console.log("🎯 Bot token received and configured for direct API mode.");
663-
}
664-
596+
665597
console.log("Next step: sudo baudbot restart");
666598

667599
return 0;

bin/broker-register.test.mjs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,38 @@ test("runRegistration integration path succeeds against live local HTTP server",
249249
}
250250
});
251251

252+
test("runRegistration does not write SLACK_BOT_TOKEN even when broker returns encrypted_bot_token", async () => {
253+
const fetchImpl = async (url) => {
254+
if (String(url).endsWith("/api/broker-pubkey")) {
255+
return jsonResponse({
256+
ok: true,
257+
broker_pubkey: Buffer.alloc(32, 9).toString("base64"),
258+
broker_signing_pubkey: Buffer.alloc(32, 8).toString("base64"),
259+
});
260+
}
261+
262+
if (String(url).endsWith("/api/register")) {
263+
return jsonResponse({
264+
ok: true,
265+
broker_pubkey: Buffer.alloc(32, 9).toString("base64"),
266+
broker_signing_pubkey: Buffer.alloc(32, 8).toString("base64"),
267+
encrypted_bot_token: "base64-encrypted-token",
268+
});
269+
}
270+
271+
return jsonResponse({ ok: false, error: "unexpected endpoint" }, 404);
272+
};
273+
274+
const result = await runRegistration({
275+
brokerUrl: "https://broker.example.com",
276+
workspaceId: "TABC12345",
277+
registrationToken: "token-from-dashboard",
278+
fetchImpl,
279+
});
280+
281+
assert.equal(result.updates.SLACK_BOT_TOKEN, undefined);
282+
});
283+
252284
test("runRegistration requires registration token", async () => {
253285
await assert.rejects(
254286
runRegistration({

pi/skills/control-agent/SKILL.md

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -255,13 +255,7 @@ curl -s -X POST http://127.0.0.1:7890/react \
255255
-d '{"channel":"CHANNEL_ID","timestamp":"msg_ts","emoji":"white_check_mark"}'
256256
```
257257

258-
**Fallback — direct Slack Web API** (only if bridge is down and `SLACK_BOT_TOKEN` is available; won't work in broker mode since the bot token lives on the broker):
259-
```bash
260-
source ~/.config/.env && curl -s -X POST https://slack.com/api/chat.postMessage \
261-
-H "Authorization: Bearer $SLACK_BOT_TOKEN" \
262-
-H 'Content-Type: application/json' \
263-
-d '{"channel":"CHANNEL_ID","text":"your message","thread_ts":"optional"}'
264-
```
258+
Broker pull mode does not support direct Slack Web API fallback. If the local bridge is down, restart the runtime/bridge first, then retry via local API.
265259

266260
### Message Context
267261

0 commit comments

Comments
 (0)