@broberg/auth-relay

中文文档
0.5.2发布于 2026/11/01ES Module近 30 天下载 5905

ARS — an agent asks the owner for a code or password; the owner answers once on a local page; only the asker gets it, once.

原始使用文档
文档目录10 节
  1. Vision
  2. Mission
  3. What it is
  4. Promises
  5. Who uses it
  6. Asking from a cc session
  7. Status
  8. Pairing — only your window can answer
  9. For the move into cardmem (cardmem-F406)
  10. Using ARS in a host (the package)

ARS — Auth Relay Service

A local page where the owner types a code or password an agent asked for — npm OTP, authenticator-app codes, logins — so the value never passes through a chat or an LLM context.

Vision

The fleet runs 100 % on its own — and the few moments that genuinely need the owner's hand (a one-time code from his phone, a password only he knows) take him five seconds on one clean page, never a terminal, never a chat, never a copy-paste.

Mission

Give every agent in the fleet one simple, safe way to ask the owner for a secret value, and give the owner one simple, safe place to answer it: the agent asks, he gets a notification, he types it once, only the asking agent receives it — once — and the value is never stored, logged or seen by an LLM.

What it is

Today, when an agent hits an npm publish that needs a one-time code, a site with two-factor login, or a password, the work stops. The owner has to notice it in a terminal, read the request, and paste the value into chat — where it lands in a transcript and an AI's memory.

ARS removes that. It is a small web service that runs on the owner's Mac, reachable only from the Mac itself:

  1. An agent asks. POST http://127.0.0.1:<port>/v1/asks with who is asking, the question, and the kind of answer (code, password, text or approve).
  2. The owner is told. A macOS notification names who asks and why, and the ask page opens in his browser.
  3. He answers. One field (masked for passwords, numeric for codes), Send or Afvis.
  4. The agent gets it, once. The agent long-polls GET /v1/asks/:id?wait=55; the first read hands out the answer and wipes it. A later read only says it was answered.

Promises

  • Never stored, never logged. Answers live only in memory until read or expired. An audit list shows who asked for what and when — never the value.
  • Expires. An ask unanswered after 10 minutes can no longer be answered.
  • Only the asker can read it. Each ask carries its own read token, and only the same BID principal that asked may read it.
  • Agents prove who they are. Every agent call carries a BrobergID ticket; the owner sees who asks from the ticket (edge m1 · session lens), never from text the agent wrote.
  • Only from this Mac — and, for agents, the fleet's private network. Bound to 127.0.0.1, with guards against web pages trying to reach it. Agents on the other Macs reach the agent API (and nothing else) on this Mac's Tailscale address; the window, history and answering stay on loopback.
  • Not a shortcut past the owner's own word. An approve ask is for routine confirmations; deploys with destructive migrations, DNS, env flips and the like still need his direct order.

Who uses it

  • Lens — a test step fill [field] from owner "<question>" asks ARS and types the answer, so a flow that hits 2FA keeps running.
  • Any cc session — ask for an npm OTP before npm publish, with one curl.

Asking from a cc session

Get a ticket from buddy (valid 300 s — fetch a new one for each call), then ask and wait:

T=$(curl -s -m 5 "http://127.0.0.1:4123/api/edge-ticket?audience=https://ars.broberg.ai&scope=ars:request&session=$BUDDY_SESSION_NAME" | jq -r .ticket)
curl -s -X POST http://127.0.0.1:7480/v1/asks -H "authorization: Bearer $T" -H 'content-type: application/json' \
  -d '{"requester":"my-session: npm publish","question":"npm OTP for @broberg/x 1.2.3","kind":"code"}'
# → { id, read_token, … }; then GET /v1/asks/<id>?wait=55 with the same kind of header + x-ars-read-token

From another Mac, use this Mac's Tailscale address instead of 127.0.0.1 (printed by scripts/install.sh, e.g. http://100.78.170.62:7480).

Pass the answer to the next command through an environment variable (npm_config_otp=…), never as text in chat.

  • Later: buzzalert — the mobile version (push to the iPhone) on the same /v1/asks contract, so consumers change only the address.

Status

Planned. See docs/PLAN.md for the contract, stories and rollout.

Pairing — only your window can answer

The first time the ARS window opens on a Mac it asks for a 6-digit code, which ARS shows only as a macOS notification. The right code pairs that window; from then on only it can see waiting asks and answer them. ARS keeps only a hash of the window's key (~/Library/Application Support/ARS/owner.json).

Honest limit: pairing stops any program from simply calling ARS and answering its own ask. It does not stop a program that runs as you and drives your browser. When BID gives Mac sessions an identity, this moves to BID.

For the move into cardmem (cardmem-F406)

Start with docs/MIGRATION.md — what cardmem takes over, the mount, history import, sound/speech, the client and the switch-off order.

  • src/core/ is the ask core (store, four states, single read, expiry, read_token). It imports only node:crypto; a test keeps it that way.
  • src/contract.test.ts is the /v1/asks contract. Run it against any host: ARS_CONTRACT_URL=http://host:port bun test src/contract.test.ts. The owner-answer cases need an adapter (ARS: ARS_CONTRACT_PAIR_CODE); without one they are reported as skipped.

Using ARS in a host (the package)

Bun only. @broberg/auth-relay ships TypeScript source (engines.bun); a Node host cannot import it. Pin it exactly — it guards secrets.

import { createAsksRoute, webRoot } from '@broberg/auth-relay';
import { serveStatic } from 'hono/bun';

app.route('/ars', createAsksRoute({
  auth: (req) => /* your agent auth */ ({ principal: 'agent-123', label: 'components' }), // null → 401
  delivery: {
    notify: ({ id, label, question, pageUrl }) => { /* tell the owner; never gets an answer */ },
    isOwner: (c) => /* your owner session check */ true,
  },
  pageUrl: (id) => `https://host/ars/?ask=${id}`,
}));
app.get('/ars', (c) => c.redirect('/ars/'));            // the window's URLs are relative
app.use('/ars/*', serveStatic({ root: webRoot, rewriteRequestPath: (p) => p.replace(/^\/ars/, '') }));
  • auth.label is what the owner sees as the requester; an answer is only readable by the principal that asked.
  • Put your own Host/JSON guard in front (ARS standalone uses localRequestGuard from @broberg/local-guard/hono).
  • If isOwner says no, the window shows ARS' pairing screen — a host with its own login should send the owner to it instead.
  • Prove your host: defineContractSuite({ baseUrl, adapter, agentHeaders, guards }) and defineNoPersistSuite(start) from @broberg/auth-relay/testing.

版本与发布记录

@broberg/auth-relay 共发布 32 个版本,首次发布于 2026/10/25;过去 12 个月发布了 32 个版本。

版本发布日期距今
0.5.2latest2026/11/017 天前
0.5.12026/11/017 天前
0.5.02026/10/318 天前
0.4.12026/10/318 天前
0.4.02026/10/318 天前
0.3.222026/10/2811 天前

关于 @broberg/auth-relay 的常见问题

@broberg/auth-relay 是什么?

ARS — an agent asks the owner for a code or password; the owner answers once on a local page; only the asker gets it, once.

如何安装 @broberg/auth-relay?

在项目目录运行 npm install @broberg/auth-relay,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 ars,可以用 npx ars 直接运行。

@broberg/auth-relay 支持 TypeScript 吗?

该包的 package.json 没有声明类型入口。需要类型提示时,可以在 npm 上查找社区维护的 @types/broberg__auth-relay 是否存在。

@broberg/auth-relay 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@broberg/auth-relay 可以商用吗?

@broberg/auth-relay 使用 未声明 许可。package.json 没有声明许可证。没有许可证的代码默认保留全部权利,商用前请联系作者或查看仓库中的 LICENSE 文件。以上为概括,不构成法律意见,请以许可原文为准。

@broberg/auth-relay 的下载量有多少?

根据 npm 官方下载统计,@broberg/auth-relay 在 2026/10/09 – 2026/11/07 这 30 天内被下载 5,905 次,日均约 197 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@broberg/auth-relay 还在维护吗?

最新版本 0.5.2 发布于 2026/11/01(7 天前);至今共发布 32 个版本,其中过去 12 个月发布了 32 个;首次发布于 2026/10/25。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。