npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@qilin-agent/accounts-local

v3.1.11

Published

Local browser accounts for the Web surface: one account file under the harness home, scrypt password hashes, signed HttpOnly session cookies, the /api/auth endpoints, and the transport's account-session gate

Readme


description: "Web 表层的本地浏览器账户:harness home 下的账户文件、scrypt 密码哈希、签名的 HttpOnly 会话 cookie、/api/auth 端点,以及覆盖应用文档与其他所有 /api 请求的账户会话门禁。" kind: "package-reference"

@qilin-agent/accounts-local

English | 中文

概述

在 Web 部署中挂载本包,浏览器必须先登录才能到达 harness。首位访问者初始化管理员账户;此后每个浏览器都用用户名或该账户携带的邮箱地址登录。账户会话门禁只把应用文档与 /api 交给本部署签发的会话,没有会话的浏览器会被送到公开 landing 页。账户文件是本部署的名册,admittedAccounts 把一份部署收窄到它列名的账户,这正是共享该文件的多个运行时之间的分隔。

目录


使用本包

把本插件与 qilin-client-connection 及一个 dist 服务器组合在一起,Web 表层就会在交出应用之前要求账户。它注入 connection 服务。

最小配置

- name: '@qilin-agent/accounts-local'
  config:
    registration: closed

| 字段 | 默认值 | 含义 | |---|---|---| | enabled | true | 要求受门禁的 index 路径与每个非公开 /api 请求都携带账户会话 | | registration | 'open' | 匿名访问者是否可以创建额外账户 | | sessionMaxAgeDays | 7 | 浏览器会话的绝对有效期,单位为天 | | sessionCookieSecure | false | 给会话 cookie 加 Secure;在公网地址上终止 TLS 的部署需要打开它 | | loginMaxAttempts | 10 | 同一标识符在窗口内累计多少次失败后,后续尝试必须等待 | | loginAttemptWindowSeconds | 900 | 这些失败计入的窗口长度,也是被拒调用方被告知要等待的时长 | | qilinHome | $QILIN_HOME,其次 ~/.qilin | 存放 auth/accounts.json 的 harness home | | admittedAccounts | 文件中的每个账户 | 本部署接纳的账户标识;未列名的账户既不能登录,其已签发的会话也不再有效 |

生成的配置目录是每个受支持字段及其 JSDoc 的穷尽式真源。

登录流程

请求受门禁 index 路径的匿名浏览器会被重定向到公开 landing 页并带上 ?next=<请求路径>,绝不会直接落到凭据表单。landing 页读取 GET /api/auth/status,把入口按钮指向本部署能提供的文档:账户文件里还没有账户时是首次运行页,否则是登录页,两者都会带上请求的路径。该文档渲染表单并提交凭据;成功响应本身已经携带会话 cookie,因此浏览器随后导航到经过校验的 next 目标或应用入口路径。因为门禁位于提供该文档的操作里,直接导航无法绕过任何客户端检查;退出登录会把浏览器送回 landing 页。同一 authority 与标识符的反复失败会让后续尝试等待:从第一次失败开始计数,窗口过去或登录成功都会清零,而不指向任何账户的标识符照同样方式计数,因此这道限制不是「名字探测」。

端点

每个端点都以 cache-control: no-store 返回 JSON,每个拒绝都是 { "error": { "code", "message" } }。

| 端点 | 应答 | |---|---| | GET /api/auth/status | 200 { enabled, needsSetup, registrationOpen, authenticated, user };user 是 { id, username, email, createdAt } 或 null,未填邮箱的账户其 email 为 null | | POST /api/auth/setup | 200 { user } 加会话 cookie;已存在账户时 409 already-initialized | | POST /api/auth/register | 200 { user } 加会话 cookie;403 registration-closed;409 username-taken 或 email-taken | | POST /api/auth/login | 200 { user } 加会话 cookie;标识符未知、密码错误或账户未被本部署接纳时 401 invalid-credentials;同一标识符在窗口内的失败达到 loginMaxAttempts 后为 429 too-many-attempts 并带 Retry-After | | POST /api/auth/logout | 204,并清除该 authority 的 cookie | | POST /api/auth/change-password | 200 { user } 加新的会话 cookie;没有会话时 401 unauthorized,当前密码错误时 401 invalid-credentials;409 username-taken 或 email-taken |

setup 与 register 读取 { username, email?, password };login 读取 { identifier, password },用户名与该账户的邮箱地址都可作为标识符;change-password 读取 { currentPassword, newPassword, username?, email? },并更换其中给出的每个身份字段,地址为空字符串即清除已存地址。用户名为 3–32 位字母、数字、点、下划线或短横线且以字母或数字开头,存储与查找前会去除空白并转为小写,因此同一账户不会被拼成两种写法;地址同样处理,密码至少 8 个字符。不是 JSON 对象的请求体、缺失的凭据字段、形式不合法的用户名或地址,以及没有指明 authority 的请求,会分别以 400 invalid-body、invalid-username、invalid-email、password-too-short 或 invalid-authority 拒绝。

账户会话门禁

enabled 为 true 时,插件在 ctx.connection.session 安置唯一的 ConnectionSessionAuthority,两个执行点都由 Connection 拥有。index 请求经 authority 的 authorizeIndex:会话有效时提供文档,其他任何请求都得到上面的 302。在共享 /api 路由上,isPublicApiRequest 放行 /api/auth/ 前缀,使浏览器能够登录;verify 要求其他所有请求都携带账户会话——缺少会话的请求在通过 connection 包的 Host 与 Origin 校验后得到 401。enabled: false 会保留全部六个端点,在状态读取中报告 enabled: false,并让启动令牌 cookie 继续掌管 index 路径与 /api 路由。


接纳名单

admittedAccounts 列出本部署服务的账户。装载时会拒绝这样的名单:没有列出任何账户、列出了账户文件里没有的标识、或没有把 registration 关掉——未被列名的账户根本无法使用这份部署,所以开放注册只会把账户交给一个进不来的访问者。拒绝覆盖两个方向:POST /api/auth/login 对不属于本部署的账户给出与密码错误完全相同的应答;已经签发的会话也不再为未被接纳的账户命名账户,尽管其签名仍然有效。因此一份共享的账户文件可以服务多个运行时:qilin tenants serve 为每个账户组合一个运行时,各自只接纳自己。

理解实现

本节说明插件拥有什么,并指出实现它们的文件;可观察行为已在使用本包中说明。

设计理念

本包是一个函数插件。apply 打开解析后 harness home 的账户文件,在该账户文件旁加载或创建该部署的会话签名密钥(auth/session-secret.json,以 0600 建在 0700 目录内),构建 cookie 所有者,在 ctx.connection.fetch 上注册六个路由,并在门禁启用时把会话 authority 安置到 ctx.connection.session——该席位只接受一个所有者,第二次安置会让安装方插件加载失败。它不提供任何 Kylin 服务:没有别的东西读取账户,组合通过端点与门禁到达这个表层。

账户文件

$QILIN_HOME/auth/accounts.json 是一份带版本的文档,为每个账户保存一条记录:不透明的 id、规范化地址、编码后的 scrypt 哈希、创建时间与凭据代数。每次变更都经 @qilin-agent/atomic-write 以 0600 模式写入完整后继内容,然后才在内存中发布,因此写入失败时运行中的服务器仍停留在上一份账户集合。文件缺失即空账户集合——首次注册初始化的就是这个状态;而本构建未曾写入的文件会让加载失败,而不是被迁移。存储层可创建账户、替换账户、用 setPassword 为某个账户重新设置密码,以及移除某个账户;qilin tenants add、passwd、remove 是针对该集合的运维命令。移除某个账户时,若其租户覆盖层仍列名该账户,该运行时会在加载时明确失败,而不是继续服务一个文件里已不存在的账户。

密码与会话

存储的哈希是自描述字符串(scrypt$N$r$p$salt$key),带逐账户 salt、代价 2^15、块大小 8、并行度 1 与 32 字节密钥;校验按存储的参数重新派生并以恒定时间比较。会话 cookie 是 HMAC-SHA256 签名,按其签发时的请求 authority 命名,并携带该 authority、账户 id、凭据代数,以及不超过 sessionMaxAgeDays 的绝对签发与过期区间。它是 host-only、Path=/、HttpOnly、SameSite=Strict,并在设置 sessionCookieSecure 时带 Secure——在公网地址上终止 TLS 的部署需要它,因为该地址背后的运行时说的是 HTTP。密码或邮箱变更会递增账户的凭据代数,因此此前签发的所有 cookie 即使签名仍能验证,也不再指向任何账户。

源码地图

| 文件 | 职责 | |---|---| | src/index.ts | Config、账户文件与会话密钥加载、路由注册、门禁安置 | | src/routes.ts | 六个 /api/auth 端点、输入校验与错误信封 | | src/gate.ts | authority 的判定:index 重定向、公开 API 前缀、会话校验 | | src/accounts.ts | 持久账户文件:解析、原子替换、凭据代数更新 | | src/password.ts | scrypt 哈希与恒定时间校验 | | src/session.ts | 签名的会话 cookie:签发、清除、authority 绑定、有效期 | | src/paths.ts | AUTH_API_PREFIX、LOGIN_PATH 与 SETUP_PATH | | src/validation.ts | 邮箱规范化与密码最小长度 | | tests/auth-surface.spec.ts | 真实组合:公开文档、受门禁入口、端点、/api 门禁、启动令牌交接 | | tests/accounts.spec.ts | 账户文件解析、变更,以及写入失败后留下的状态 | | tests/password.spec.ts | 哈希编码、校验,以及对外来存储值的拒绝 | | tests/session.spec.ts | cookie 属性、签名、authority 绑定与有效期 |


进一步探索

当包级约定不够用时阅读以下内容:先看拥有门禁的 transport,再看挂载本行的组合。


模型体验

无,因为账户只为浏览器表层设置门禁,本包不注册任何面向模型的内容。

KV Cache 影响

无;该包既不组装也不发送提供方请求。

已知限制与延期工作

这些限制说明一个账户保护什么、不保护什么。它们是当前包约束,不是任务积压。

  • 账户集合属于单个 harness home——每个账户都能到达与首个账户相同的 Session、凭据与文件,因此第二个账户只是同一套 harness 的另一把钥匙,而不是独立租户;不同的人需要各自的 QILIN_HOME 与端口。admittedAccounts 把一份部署收窄到该文件的一个子集,它分隔的是运行时而不是账户。
  • 接纳分隔的是运行时,不是操作系统用户——所有运行时都以同一个操作系统用户运行,因此某个租户内的 shell 或文件工具能读到另一个租户的目录与共享凭据。
  • 注册默认开放——在账户集合仍有空位时,任何能到达该端口的人都可以创建拥有完整 harness 访问权限的账户;绑定到 loopback 之外的部署要设置 registration: closed。随附的 qilin web 命令绑定 loopback 并拒绝 --host 0.0.0.0。
  • 登录限流按 authority 与标识符,而不是按部署——同一键在 loginAttemptWindowSeconds 内失败达到 loginMaxAttempts 次后,它要等到窗口结束。轮换标识符的攻击者遇不到共享限制,因为本部署不保留按 authority 汇总的计数。
  • 没有账户删除或密码找回——没有任何端点会删除账户或找回遗忘的密码;操作者需要编辑或删除 $QILIN_HOME/auth/accounts.json,删除该文件会让部署回到首次运行状态。
  • 会话 cookie 是 bearer 凭据——它是 HttpOnly; SameSite=Strict,仅在设置 sessionCookieSecure 时带 Secure;在公网地址上终止 TLS 的部署需要它,而浏览器以明文 HTTP 到达的部署也只能以明文回传。
  • 会话密钥丢失或被替换会终结所有会话——名册旁的 auth/session-secret.json 是唯一的签名密钥,且该部署的每个运行时都读这同一份文档,因此一个运行时签发的会话会被下一个接受;替换它会作废所有已签发的 cookie,而账户本身保留。
  • 账户文件只能向前迁移——版本 1 的文档会被读成「用户名即其所存地址」的账户,但来自更新构建的文档会让插件加载失败,且在下次变更之前不会有任何东西改写版本 1 的文件。看起来像地址的迁移后用户名,必须由运营者手工修改才能换成合法用户名。

开发备注

无。