@aventara/access 0.0.0-stage → 1.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. package/CHANGELOG.md +877 -0
  2. package/LICENSE +91 -0
  3. package/LICENSE-ADDITIONAL-PERMISSION.md +9 -0
  4. package/README.md +2536 -2
  5. package/dist/access/access-options.d.ts +74 -0
  6. package/dist/access/access-options.js +5 -0
  7. package/dist/access/access.error.d.ts +35 -0
  8. package/dist/access/access.error.js +11 -0
  9. package/dist/access/access.factory.d.ts +275 -0
  10. package/dist/access/access.factory.js +440 -0
  11. package/dist/access/better-auth.hardening.d.ts +120 -0
  12. package/dist/access/better-auth.hardening.js +420 -0
  13. package/dist/access/better-auth.loader.d.ts +67 -0
  14. package/dist/access/better-auth.loader.js +65 -0
  15. package/dist/access/better-auth.range.d.ts +9 -0
  16. package/dist/access/better-auth.range.js +28 -0
  17. package/dist/access/better-auth.require.d.ts +90 -0
  18. package/dist/access/better-auth.require.js +6 -0
  19. package/dist/access/better-auth.require.node.d.ts +7 -0
  20. package/dist/access/better-auth.require.node.js +52 -0
  21. package/dist/access/claims.mapper.d.ts +79 -0
  22. package/dist/access/claims.mapper.js +152 -0
  23. package/dist/access/key-set.cache.d.ts +10 -0
  24. package/dist/access/key-set.cache.js +63 -0
  25. package/dist/access/policies-block.checker.d.ts +64 -0
  26. package/dist/access/policies-block.checker.js +138 -0
  27. package/dist/access/principal.subject.d.ts +22 -0
  28. package/dist/access/principal.subject.js +89 -0
  29. package/dist/access/profile.policy.d.ts +74 -0
  30. package/dist/access/profile.policy.js +110 -0
  31. package/dist/access/request.state.d.ts +89 -0
  32. package/dist/access/request.state.js +266 -0
  33. package/dist/access/session.hooks.d.ts +114 -0
  34. package/dist/access/session.hooks.js +258 -0
  35. package/dist/access/token.verifier.d.ts +35 -0
  36. package/dist/access/token.verifier.js +69 -0
  37. package/dist/access-schema.d.ts +48 -0
  38. package/dist/access-schema.js +5 -0
  39. package/dist/access-warning.emitter.d.ts +4 -0
  40. package/dist/access-warning.emitter.js +24 -0
  41. package/dist/access.builder.d.ts +340 -0
  42. package/dist/access.builder.js +75 -0
  43. package/dist/access.engine.d.ts +155 -0
  44. package/dist/access.engine.js +477 -0
  45. package/dist/audit/audit-event.d.ts +135 -0
  46. package/dist/audit/audit-event.js +8 -0
  47. package/dist/audit/audit.log.d.ts +122 -0
  48. package/dist/audit/audit.log.js +345 -0
  49. package/dist/audit/audit.schema.d.ts +143 -0
  50. package/dist/audit/audit.schema.js +50 -0
  51. package/dist/auth/index.d.ts +12 -0
  52. package/dist/auth/index.js +2 -0
  53. package/dist/aventara-where.translator.d.ts +19 -0
  54. package/dist/aventara-where.translator.js +87 -0
  55. package/dist/beacon/beacon.check.d.ts +59 -0
  56. package/dist/beacon/beacon.check.js +128 -0
  57. package/dist/beacon/beacon.schema.d.ts +41 -0
  58. package/dist/beacon/beacon.schema.js +48 -0
  59. package/dist/beacon/beacon.store.d.ts +29 -0
  60. package/dist/beacon/beacon.store.js +78 -0
  61. package/dist/better-auth/access.client-plugin.d.ts +75 -0
  62. package/dist/better-auth/access.client-plugin.js +133 -0
  63. package/dist/better-auth/access.plugin.d.ts +164 -0
  64. package/dist/better-auth/access.plugin.js +348 -0
  65. package/dist/better-auth/access.provider.d.ts +80 -0
  66. package/dist/better-auth/access.provider.js +100 -0
  67. package/dist/better-auth/index.d.ts +7 -0
  68. package/dist/better-auth/index.js +4 -0
  69. package/dist/better-auth/organization.bridge.d.ts +66 -0
  70. package/dist/better-auth/organization.bridge.js +144 -0
  71. package/dist/canonical-json.writer.d.ts +12 -0
  72. package/dist/canonical-json.writer.js +30 -0
  73. package/dist/cli/avaccess.bin.d.ts +2 -0
  74. package/dist/cli/avaccess.bin.js +26 -0
  75. package/dist/cli/avaccess.program.d.ts +15 -0
  76. package/dist/cli/avaccess.program.js +166 -0
  77. package/dist/cli/beacon-pins.file.d.ts +14 -0
  78. package/dist/cli/beacon-pins.file.js +62 -0
  79. package/dist/cli/beacon.loader.d.ts +20 -0
  80. package/dist/cli/beacon.loader.js +71 -0
  81. package/dist/cli/cli.context.d.ts +49 -0
  82. package/dist/cli/cli.context.js +66 -0
  83. package/dist/cli/diff.command.d.ts +2 -0
  84. package/dist/cli/diff.command.js +66 -0
  85. package/dist/cli/generate.command.d.ts +2 -0
  86. package/dist/cli/generate.command.js +82 -0
  87. package/dist/cli/keys.command.d.ts +2 -0
  88. package/dist/cli/keys.command.js +96 -0
  89. package/dist/cli/lock.file.d.ts +31 -0
  90. package/dist/cli/lock.file.js +181 -0
  91. package/dist/cli/module.loader.d.ts +13 -0
  92. package/dist/cli/module.loader.js +63 -0
  93. package/dist/cli/policies.diff.d.ts +16 -0
  94. package/dist/cli/policies.diff.js +101 -0
  95. package/dist/cli/pull.command.d.ts +2 -0
  96. package/dist/cli/pull.command.js +234 -0
  97. package/dist/cli/push.command.d.ts +2 -0
  98. package/dist/cli/push.command.js +59 -0
  99. package/dist/cli/registry.client.d.ts +63 -0
  100. package/dist/cli/registry.client.js +287 -0
  101. package/dist/cli/schema.command.d.ts +2 -0
  102. package/dist/cli/schema.command.js +60 -0
  103. package/dist/cli/serve.command.d.ts +6 -0
  104. package/dist/cli/serve.command.js +200 -0
  105. package/dist/cli/status.command.d.ts +2 -0
  106. package/dist/cli/status.command.js +31 -0
  107. package/dist/cli/translator.flag.d.ts +6 -0
  108. package/dist/cli/translator.flag.js +24 -0
  109. package/dist/cli/verify.command.d.ts +2 -0
  110. package/dist/cli/verify.command.js +40 -0
  111. package/dist/client/access-client.factory.d.ts +92 -0
  112. package/dist/client/access-client.factory.js +269 -0
  113. package/dist/client/access-verbs.schema.d.ts +66 -0
  114. package/dist/client/access-verbs.schema.js +159 -0
  115. package/dist/client/verb-rate.limiter.d.ts +116 -0
  116. package/dist/client/verb-rate.limiter.js +358 -0
  117. package/dist/foreign/foreign-facts.d.ts +30 -0
  118. package/dist/foreign/foreign-facts.js +75 -0
  119. package/dist/foreign/foreign.merger.d.ts +10 -0
  120. package/dist/foreign/foreign.merger.js +52 -0
  121. package/dist/foreign-references.check.d.ts +6 -0
  122. package/dist/foreign-references.check.js +131 -0
  123. package/dist/generator/client.emitter.d.ts +28 -0
  124. package/dist/generator/client.emitter.js +411 -0
  125. package/dist/generator/copied-modules.reader.d.ts +14 -0
  126. package/dist/generator/copied-modules.reader.js +80 -0
  127. package/dist/generator/namespace.facts.d.ts +28 -0
  128. package/dist/generator/namespace.facts.js +32 -0
  129. package/dist/generator/orm.translator.d.ts +48 -0
  130. package/dist/generator/orm.translator.js +29 -0
  131. package/dist/generator/prisma7/prisma-schema.reader.d.ts +9 -0
  132. package/dist/generator/prisma7/prisma-schema.reader.js +394 -0
  133. package/dist/generator/prisma7/prisma-where.translator.d.ts +73 -0
  134. package/dist/generator/prisma7/prisma-where.translator.js +138 -0
  135. package/dist/generator/prisma7/prisma7.translator.d.ts +2 -0
  136. package/dist/generator/prisma7/prisma7.translator.js +87 -0
  137. package/dist/generator/schema.emitter.d.ts +14 -0
  138. package/dist/generator/schema.emitter.js +23 -0
  139. package/dist/generator/tsconfig.reader.d.ts +44 -0
  140. package/dist/generator/tsconfig.reader.js +110 -0
  141. package/dist/generator/typescript-literal.writer.d.ts +5 -0
  142. package/dist/generator/typescript-literal.writer.js +42 -0
  143. package/dist/guardrails/guardrails-contract.d.ts +45 -0
  144. package/dist/guardrails/guardrails-contract.js +12 -0
  145. package/dist/guardrails/guardrails.builder.d.ts +48 -0
  146. package/dist/guardrails/guardrails.builder.js +32 -0
  147. package/dist/guardrails/guardrails.compiler.d.ts +10 -0
  148. package/dist/guardrails/guardrails.compiler.js +254 -0
  149. package/dist/guardrails/guardrails.composer.d.ts +7 -0
  150. package/dist/guardrails/guardrails.composer.js +95 -0
  151. package/dist/guardrails/guardrails.hash.d.ts +10 -0
  152. package/dist/guardrails/guardrails.hash.js +38 -0
  153. package/dist/index.d.ts +16 -0
  154. package/dist/index.js +6 -0
  155. package/dist/kiosk/kiosk-verbs.schema.d.ts +19 -0
  156. package/dist/kiosk/kiosk-verbs.schema.js +61 -0
  157. package/dist/kiosk/kiosk.plugin.d.ts +217 -0
  158. package/dist/kiosk/kiosk.plugin.js +877 -0
  159. package/dist/kiosk/kiosk.schema.d.ts +176 -0
  160. package/dist/kiosk/kiosk.schema.js +101 -0
  161. package/dist/kiosk/kiosk.store.d.ts +171 -0
  162. package/dist/kiosk/kiosk.store.js +478 -0
  163. package/dist/kiosk/lan-range.policy.d.ts +31 -0
  164. package/dist/kiosk/lan-range.policy.js +113 -0
  165. package/dist/kiosk/pin.policy.d.ts +9 -0
  166. package/dist/kiosk/pin.policy.js +143 -0
  167. package/dist/kiosk/session-use.marker.d.ts +4 -0
  168. package/dist/kiosk/session-use.marker.js +16 -0
  169. package/dist/lock/lock-contract.d.ts +38 -0
  170. package/dist/lock/lock-contract.js +67 -0
  171. package/dist/lock/lock.verifier.d.ts +27 -0
  172. package/dist/lock/lock.verifier.js +62 -0
  173. package/dist/mcp/agent-scope.gate.d.ts +13 -0
  174. package/dist/mcp/agent-scope.gate.js +75 -0
  175. package/dist/mcp/agent.grant.d.ts +40 -0
  176. package/dist/mcp/agent.grant.js +92 -0
  177. package/dist/mcp/mcp-options.d.ts +60 -0
  178. package/dist/mcp/mcp-options.js +72 -0
  179. package/dist/oauth/access-token.verifier.d.ts +66 -0
  180. package/dist/oauth/access-token.verifier.js +201 -0
  181. package/dist/oauth/oauth-provider.hardening.d.ts +62 -0
  182. package/dist/oauth/oauth-provider.hardening.js +261 -0
  183. package/dist/passwords/argon2id.hasher.d.ts +97 -0
  184. package/dist/passwords/argon2id.hasher.js +256 -0
  185. package/dist/passwords/node-rs-argon2.adapter.d.ts +25 -0
  186. package/dist/passwords/node-rs-argon2.adapter.js +42 -0
  187. package/dist/passwords/passwords.options.d.ts +79 -0
  188. package/dist/passwords/passwords.options.js +212 -0
  189. package/dist/policies-contract.d.ts +219 -0
  190. package/dist/policies-contract.js +8 -0
  191. package/dist/policies.compiler.d.ts +38 -0
  192. package/dist/policies.compiler.js +1433 -0
  193. package/dist/policies.decider.d.ts +103 -0
  194. package/dist/policies.decider.js +390 -0
  195. package/dist/policies.hash.d.ts +10 -0
  196. package/dist/policies.hash.js +54 -0
  197. package/dist/predicate.evaluator.d.ts +62 -0
  198. package/dist/predicate.evaluator.js +378 -0
  199. package/dist/random-secret.generator.d.ts +2 -0
  200. package/dist/random-secret.generator.js +15 -0
  201. package/dist/read-governed-unique.check.d.ts +3 -0
  202. package/dist/read-governed-unique.check.js +21 -0
  203. package/dist/reference-reads.check.d.ts +16 -0
  204. package/dist/reference-reads.check.js +158 -0
  205. package/dist/registry/access.registry.d.ts +173 -0
  206. package/dist/registry/access.registry.js +865 -0
  207. package/dist/registry/beacon.ledger.d.ts +30 -0
  208. package/dist/registry/beacon.ledger.js +93 -0
  209. package/dist/registry/ownership.check.d.ts +54 -0
  210. package/dist/registry/ownership.check.js +221 -0
  211. package/dist/registry/registry.endpoints.d.ts +50 -0
  212. package/dist/registry/registry.endpoints.js +193 -0
  213. package/dist/registry/registry.key.d.ts +7 -0
  214. package/dist/registry/registry.key.js +13 -0
  215. package/dist/registry/registry.options.d.ts +13 -0
  216. package/dist/registry/registry.options.js +28 -0
  217. package/dist/registry/registry.refusal.d.ts +6 -0
  218. package/dist/registry/registry.refusal.js +11 -0
  219. package/dist/registry/registry.schema.d.ts +238 -0
  220. package/dist/registry/registry.schema.js +127 -0
  221. package/dist/resources/auth-tables.catalog.d.ts +96 -0
  222. package/dist/resources/auth-tables.catalog.js +110 -0
  223. package/dist/resources/auth-tables.guard.d.ts +106 -0
  224. package/dist/resources/auth-tables.guard.js +462 -0
  225. package/dist/scope.resolver.d.ts +144 -0
  226. package/dist/scope.resolver.js +529 -0
  227. package/dist/sha256.digest.d.ts +2 -0
  228. package/dist/sha256.digest.js +81 -0
  229. package/dist/standalone/access.server.d.ts +41 -0
  230. package/dist/standalone/access.server.js +301 -0
  231. package/dist/standalone/index.d.ts +1 -0
  232. package/dist/standalone/index.js +1 -0
  233. package/package.json +102 -3
  234. package/skill/SKILL.md +170 -0
  235. package/skill/reference/callers.md +99 -0
  236. package/skill/reference/errors.md +89 -0
  237. package/skill/reference/recipes.md +281 -0
  238. package/skill/reference/rules.md +153 -0
  239. package/skill/reference/setup.md +334 -0
  240. package/src/aventara-where.translator.ts +116 -0
  241. package/src/beacon/beacon.check.ts +204 -0
  242. package/src/canonical-json.writer.ts +51 -0
  243. package/src/generator/prisma7/prisma-where.translator.ts +243 -0
  244. package/src/policies-contract.ts +215 -0
  245. package/src/policies.decider.ts +662 -0
  246. package/src/predicate.evaluator.ts +574 -0
  247. package/src/scope.resolver.ts +819 -0
  248. package/src/sha256.digest.ts +85 -0
@@ -0,0 +1,143 @@
1
+ // What a PIN may be. A PIN is a weak secret: it is only ever checked on an enrolled
2
+ // device, under per-device and per-user failure limits (`kiosk.store.ts`), so it is
3
+ // short; it is still held to NIST SP 800-63B's floor for a secret that unlocks a
4
+ // device-bound authenticator (six digits, section 5.1.9.1), and the values guessed
5
+ // first are refused (NIST SP 800-63B 5.1.1.2's blocklist of commonly used values).
6
+ //
7
+ // The blocklist is the one Apple's iOS uses for 6-digit passcodes, as Markert et al.
8
+ // extracted and described it, widened to every length a PIN may have, plus the most
9
+ // common 6-digit PINs of the leaked-password data:
10
+ //
11
+ // - Markert, Bailey, Golla, Dürmuth, Aviv, "This PIN Can Be Easily Guessed: Analyzing
12
+ // the Security of Smartphone Unlock PINs", IEEE S&P 2020, and its data
13
+ // (https://this-pin-can-be-easily-guessed.github.io): the iOS 6-digit blocklist
14
+ // (2910 PINs) is exactly the patterns `aaaaaa`, `abcabc`, `abccba`, `ababab`,
15
+ // `ababa?`, 14 series and 6 common PINs, every one of which `pinDefect` refuses;
16
+ // and the 29 most common 6-digit PINs of the RockYou leak (the paper's data-driven list of 29).
17
+ // - Wang, Gu, Huang, Wang, "Understanding Human-Chosen PINs: Characteristics,
18
+ // Distribution and Security", ASIA CCS 2017: the common PINs iOS takes from it.
19
+ //
20
+ // DataGenetics' well-known list is of 4-digit PINs, shorter than any PIN here. Dates
21
+ // are not refused: they are a tenth of the 6-digit space, and the failure limits,
22
+ // not the blocklist, are what hold a guesser to a few tries.
23
+ /** The shortest and longest PIN, in digits. */
24
+ export const PIN_LENGTH = { min: 6, max: 12 };
25
+ const DIGITS = /^[0-9]+$/;
26
+ /**
27
+ * Common PINs no pattern below covers: the RockYou top 29 and iOS's series and
28
+ * common PINs, less those the patterns refuse already.
29
+ */
30
+ const COMMON_PINS = new Set([
31
+ // RockYou's most common 6-digit PINs (Markert et al.'s data-driven list).
32
+ "789456",
33
+ "123654",
34
+ "246810",
35
+ "159357",
36
+ "147852",
37
+ "159753",
38
+ "147258",
39
+ // iOS's series.
40
+ "098765",
41
+ "321654",
42
+ // iOS's common PINs (Wang et al.).
43
+ "030379",
44
+ "101471",
45
+ "520131",
46
+ ]);
47
+ /**
48
+ * The phone keypad's lines of three, both ways: rows, columns, diagonals, and the
49
+ * middle column down to 0. A PIN made of them only (`147258`, `159753`, `789456`) is
50
+ * a shape on the keypad, not a number.
51
+ */
52
+ const KEYPAD_LINES = new Set([
53
+ "123",
54
+ "456",
55
+ "789",
56
+ "147",
57
+ "258",
58
+ "369",
59
+ "159",
60
+ "357",
61
+ "580",
62
+ "280",
63
+ ].flatMap((line) => [line, [...line].reverse().join("")]));
64
+ /** Whether `pin` repeats itself with a period at most half its length (`121212`, `123123`). */
65
+ const periodic = (pin, upTo) => {
66
+ for (let period = 1; period <= upTo; period += 1) {
67
+ let repeats = true;
68
+ for (let at = period; at < pin.length && repeats; at += 1) {
69
+ repeats = pin[at] === pin[at - period];
70
+ }
71
+ if (repeats) {
72
+ return true;
73
+ }
74
+ }
75
+ return false;
76
+ };
77
+ /** Whether `pin` reads the same backwards (`123321`). */
78
+ const palindrome = (pin) => pin === [...pin].reverse().join("");
79
+ /** Whether `pin` is a straight run up or down (`123456`, `987654`). */
80
+ const straightRun = (pin) => {
81
+ let up = true;
82
+ let down = true;
83
+ for (let at = 1; at < pin.length; at += 1) {
84
+ const step = pin.charCodeAt(at) - pin.charCodeAt(at - 1);
85
+ up &&= step === 1;
86
+ down &&= step === -1;
87
+ }
88
+ return up || down;
89
+ };
90
+ /** Whether `pin` is equal runs of one digit each, two or more long (`112233`, `111222`). */
91
+ const doubledDigits = (pin) => {
92
+ for (let size = 2; size <= pin.length / 2; size += 1) {
93
+ if (pin.length % size !== 0) {
94
+ continue;
95
+ }
96
+ let runs = true;
97
+ for (let at = 0; at < pin.length && runs; at += 1) {
98
+ runs = pin[at] === pin[at - (at % size)];
99
+ }
100
+ if (runs) {
101
+ return true;
102
+ }
103
+ }
104
+ return false;
105
+ };
106
+ /** Whether `pin` is keypad lines of three only (`147258`, `159753`, `147258369`). */
107
+ const keypadShape = (pin) => {
108
+ if (pin.length % 3 !== 0) {
109
+ return false;
110
+ }
111
+ for (let at = 0; at < pin.length; at += 3) {
112
+ if (!KEYPAD_LINES.has(pin.slice(at, at + 3))) {
113
+ return false;
114
+ }
115
+ }
116
+ return true;
117
+ };
118
+ /** Why a PIN is refused at `setPin`, or `undefined` when it is accepted. */
119
+ export const pinDefect = (pin) => {
120
+ if (typeof pin !== "string" ||
121
+ !DIGITS.test(pin) ||
122
+ pin.length < PIN_LENGTH.min ||
123
+ pin.length > PIN_LENGTH.max) {
124
+ return `A PIN is ${PIN_LENGTH.min} to ${PIN_LENGTH.max} digits.`;
125
+ }
126
+ const guessable = COMMON_PINS.has(pin) ||
127
+ // One digit repeated, or a block repeated (`000000`, `121212`, `123123`).
128
+ periodic(pin, Math.floor(pin.length / 2)) ||
129
+ // Two digits alternating, then any last digit (iOS's `ababa?`).
130
+ periodic(pin.slice(0, -1), 2) ||
131
+ palindrome(pin) ||
132
+ straightRun(pin) ||
133
+ doubledDigits(pin) ||
134
+ keypadShape(pin);
135
+ return guessable
136
+ ? "That PIN is one of the first a guesser tries (a common PIN, a repeated or mirrored pattern, a straight run, or a shape on the keypad): choose another."
137
+ : undefined;
138
+ };
139
+ /** Whether a PIN could be one `setPin` accepted: its shape only, for a switch. */
140
+ export const pinShaped = (pin) => typeof pin === "string" &&
141
+ DIGITS.test(pin) &&
142
+ pin.length >= PIN_LENGTH.min &&
143
+ pin.length <= PIN_LENGTH.max;
@@ -0,0 +1,4 @@
1
+ /** On a Better Auth API call's input: the request it reads the session for may change something. */
2
+ export declare const SESSION_USE: unique symbol;
3
+ /** Whether a request with this method (upper-cased; `undefined` when unknown) may change something. */
4
+ export declare const mayChange: (method: string | undefined) => boolean;
@@ -0,0 +1,16 @@
1
+ // What counts as a till's use of its session, for the kiosk profile's idle timer: a
2
+ // request that may change something (any method but GET, HEAD and OPTIONS, or one
3
+ // whose method is unknown), never a read. A page left open on a till keeps reading
4
+ // (Better Auth's client polls `/get-session`, a dashboard refreshes), and those reads
5
+ // must not keep its session alive for whoever walks up next.
6
+ //
7
+ // `access.state` reads the session through Better Auth's API, where the hook sees
8
+ // `/get-session` and no request: it marks the call with {@link SESSION_USE} when the
9
+ // framework's request is a write. The mark is a symbol, so no caller of the API can
10
+ // spell it by accident.
11
+ /** On a Better Auth API call's input: the request it reads the session for may change something. */
12
+ export const SESSION_USE = Symbol("aventara-access.session-use");
13
+ /** The methods that change nothing. */
14
+ const READS = new Set(["GET", "HEAD", "OPTIONS"]);
15
+ /** Whether a request with this method (upper-cased; `undefined` when unknown) may change something. */
16
+ export const mayChange = (method) => method === undefined || !READS.has(method);
@@ -0,0 +1,38 @@
1
+ /** The format marker of every lock file. */
2
+ export declare const LOCK_FORMAT = "aventara-access/lock@1";
3
+ /** The lock's file name, at the project's root. */
4
+ export declare const LOCK_FILE = "avAccess.lock.json";
5
+ /** One pulled file: where it is, relative to the lock, and the hash it was pulled under. */
6
+ export type AccessLockPin = {
7
+ /** The file's path from the lock's directory, with `/` separators: `access/campaign.policies.json`. */
8
+ readonly path: string;
9
+ /** Its `policiesHash` or `guardrailsHash`: `sha256:<hex>`. */
10
+ readonly hash: string;
11
+ };
12
+ /**
13
+ * `avAccess.lock.json`, as `avaccess pull` writes it. `format` is typed as a string,
14
+ * not its literal, so the file imported as JSON (`import lock from
15
+ * "./avAccess.lock.json" with { type: "json" }`) is one as it is; it is checked when
16
+ * the engine or `avaccess verify` reads it.
17
+ */
18
+ export type AccessLock = {
19
+ /** `"aventara-access/lock@1"`. */
20
+ readonly format: string;
21
+ /** The namespace whose policies file is pinned. */
22
+ readonly namespace: string;
23
+ /** The namespace's policies file. */
24
+ readonly policies: AccessLockPin;
25
+ /** The org's guardrails file, or `null` when the registry had none at pull. */
26
+ readonly guardrails: AccessLockPin | null;
27
+ /**
28
+ * The foreign facts `avaccess pull --foreign` wrote (`access/foreign.json`): the
29
+ * schema entries of the other namespaces' Resources the rules read. Absent when
30
+ * none were pulled.
31
+ */
32
+ readonly foreign?: AccessLockPin;
33
+ };
34
+ /**
35
+ * Checks that `lock` is an `aventara-access/lock@1` lock, naming what is wrong. A
36
+ * pin's path stays inside the lock's directory.
37
+ */
38
+ export declare const checkLock: (lock: unknown, where?: string) => AccessLock;
@@ -0,0 +1,67 @@
1
+ // `avAccess.lock.json`, format `aventara-access/lock@1`: what a service is built with.
2
+ // `avaccess pull` writes the namespace's current policies file and the org's
3
+ // guardrails file into the project and pins each one's hash here; the project
4
+ // commits all three. From then on nothing at build or start touches the network:
5
+ // `avaccess verify` and the engine hash the files on disk and hold them to these pins
6
+ // (`lock.verifier.ts`).
7
+ //
8
+ // The lock pins content, not a registry: the same lock holds against any registry
9
+ // that has those revisions. The registry's address and key come from the
10
+ // environment when the CLI talks to it.
11
+ import { fail, isNamespace, isRecord } from "../policies.compiler.js";
12
+ /** The format marker of every lock file. */
13
+ export const LOCK_FORMAT = "aventara-access/lock@1";
14
+ /** The lock's file name, at the project's root. */
15
+ export const LOCK_FILE = "avAccess.lock.json";
16
+ const HASH = /^sha256:[0-9a-f]{64}$/;
17
+ /** The members a lock has, and a pin's. Anything else is refused. */
18
+ const LOCK_KEYS = ["format", "namespace", "policies", "guardrails", "foreign"];
19
+ const PIN_KEYS = ["path", "hash"];
20
+ /** What a pin's path never holds: a backslash, a control character or a line separator. */
21
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: the characters it refuses
22
+ const UNSAFE = /[\\\u0000-\u001f\u007f-\u009f\u2028\u2029]/;
23
+ /**
24
+ * Whether a pin's path stays inside the lock's directory on every platform: relative,
25
+ * `/`-separated, no `..`, no drive letter, and nothing that would end a line where
26
+ * it is printed or written into a comment.
27
+ */
28
+ const isPinPath = (path) => typeof path === "string" &&
29
+ path !== "" &&
30
+ !path.startsWith("/") &&
31
+ !/^[A-Za-z]:/.test(path) &&
32
+ !UNSAFE.test(path) &&
33
+ !path.split("/").includes("..");
34
+ const isPin = (value) => isRecord(value) &&
35
+ Object.keys(value).every((key) => PIN_KEYS.includes(key)) &&
36
+ isPinPath(value.path) &&
37
+ typeof value.hash === "string" &&
38
+ HASH.test(value.hash);
39
+ /**
40
+ * Checks that `lock` is an `aventara-access/lock@1` lock, naming what is wrong. A
41
+ * pin's path stays inside the lock's directory.
42
+ */
43
+ export const checkLock = (lock, where = "lock") => {
44
+ if (!isRecord(lock)) {
45
+ return fail(where, `${LOCK_FILE} holds an object, as \`avaccess pull\` writes it`);
46
+ }
47
+ if (lock.format !== LOCK_FORMAT) {
48
+ fail(where, `the lock's format is ${JSON.stringify(LOCK_FORMAT)}; it was ${JSON.stringify(lock.format)}`);
49
+ }
50
+ const unknown = Object.keys(lock).filter((key) => !LOCK_KEYS.includes(key));
51
+ if (unknown.length > 0) {
52
+ fail(where, `a lock has ${LOCK_KEYS.map((key) => `\`${key}\``).join(", ")}; it does not take ${unknown.map((key) => `\`${key}\``).join(", ")}`);
53
+ }
54
+ if (typeof lock.namespace !== "string" || !isNamespace(lock.namespace)) {
55
+ fail(where, `the lock's namespace ${JSON.stringify(lock.namespace)} is not a namespace name`);
56
+ }
57
+ if (!isPin(lock.policies)) {
58
+ fail(where, "the lock's `policies` is `{ path, hash }`: a relative path inside the project and a `sha256:` hash");
59
+ }
60
+ if (lock.guardrails !== null && !isPin(lock.guardrails)) {
61
+ fail(where, "the lock's `guardrails` is `{ path, hash }` (a relative path inside the project and a `sha256:` hash) or `null`");
62
+ }
63
+ if (lock.foreign !== undefined && !isPin(lock.foreign)) {
64
+ fail(where, "the lock's `foreign` is `{ path, hash }`: a relative path inside the project and a `sha256:` hash");
65
+ }
66
+ return lock;
67
+ };
@@ -0,0 +1,27 @@
1
+ import type { AccessPolicies } from "../policies-contract.js";
2
+ import { type AccessLock } from "./lock-contract.js";
3
+ /** What is held to a lock: the namespace the rules name, and the pulled files. */
4
+ type LockedFiles = {
5
+ /** The namespace the rules or the policies file name (`undefined`: none). */
6
+ readonly namespace: string | undefined;
7
+ /**
8
+ * The namespace's pulled policies file; left out when the engine runs from the
9
+ * typed rules, whose source is the project's own.
10
+ */
11
+ readonly policies?: unknown;
12
+ /** The org's pulled guardrails file, or `undefined` when none is used. */
13
+ readonly guardrails: unknown;
14
+ };
15
+ /**
16
+ * Holds `files` to `lock`: the same namespace, the policies file (when given) and
17
+ * the guardrails file each hashing to its pin, and guardrails used exactly when the
18
+ * lock pins some. Throws `ACCESS_LOCK_MISMATCH`, naming the file and both hashes.
19
+ * The lock is checked first (`checkLock`).
20
+ */
21
+ export declare const verifyLocked: (lock: AccessLock, files: LockedFiles) => void;
22
+ /**
23
+ * The namespaces whose Resources a policies file reads as foreign, sorted: those
24
+ * whose beacons a service started from a lock checks (`checkBeacons`).
25
+ */
26
+ export declare const foreignOwnersOf: (policies: AccessPolicies) => readonly string[];
27
+ export {};
@@ -0,0 +1,62 @@
1
+ // The check of pulled files against `avAccess.lock.json`, offline: each file is hashed
2
+ // with the same code the registry keys its revisions by (`policiesHash`,
3
+ // `guardrailsHash`) and held to the hash the lock pins for it. `avaccess verify` runs
4
+ // it over the files on disk, in a build's `aventara:prepare` chain; the engine runs
5
+ // it again over the files it is built with (`createAccessEngine(file, { lock })`).
6
+ // A file edited after `avaccess pull` fails both, with `ACCESS_LOCK_MISMATCH`.
7
+ import { AccessError } from "../access/access.error.js";
8
+ import { guardrailsHash } from "../guardrails/guardrails.hash.js";
9
+ import { policiesHash } from "../policies.hash.js";
10
+ import { LOCK_FILE, } from "./lock-contract.js";
11
+ const mismatch = (message) => {
12
+ throw new AccessError("ACCESS_LOCK_MISMATCH", message);
13
+ };
14
+ const AGAIN = "Pull it again with `avaccess pull`; a pulled file is never edited by hand";
15
+ /** The hash of a pulled file, or a mismatch naming why it has none (it is no file the engine runs from). */
16
+ const hashOf = (pin, file, hash) => {
17
+ try {
18
+ return hash(file);
19
+ }
20
+ catch (error) {
21
+ return mismatch(`${pin.path} is pinned in ${LOCK_FILE} under ${pin.hash}, and is no longer a file the engine runs from (${String(error.message).replace(/^Aventara Access: /, "")}): it was changed after it was pulled. ${AGAIN}`);
22
+ }
23
+ };
24
+ const holdTo = (pin, file, hash) => {
25
+ const found = hashOf(pin, file, hash);
26
+ if (found !== pin.hash) {
27
+ mismatch(`${pin.path} hashes to ${found}, and ${LOCK_FILE} pins ${pin.hash}: it was changed after it was pulled. ${AGAIN}`);
28
+ }
29
+ };
30
+ /**
31
+ * Holds `files` to `lock`: the same namespace, the policies file (when given) and
32
+ * the guardrails file each hashing to its pin, and guardrails used exactly when the
33
+ * lock pins some. Throws `ACCESS_LOCK_MISMATCH`, naming the file and both hashes.
34
+ * The lock is checked first (`checkLock`).
35
+ */
36
+ export const verifyLocked = (lock, files) => {
37
+ if (files.policies !== undefined) {
38
+ holdTo(lock.policies, files.policies, policiesHash);
39
+ }
40
+ if (files.namespace !== lock.namespace) {
41
+ mismatch(files.namespace === undefined
42
+ ? `${LOCK_FILE} pins namespace "${lock.namespace}", and the rules name no namespace: write \`definePolicies(model, { namespace: "${lock.namespace}" })\``
43
+ : `${LOCK_FILE} pins namespace "${lock.namespace}", and the rules are namespace "${files.namespace}"'s: pull this project's own namespace (\`avaccess pull --namespace ${files.namespace}\`)`);
44
+ }
45
+ if (lock.guardrails === null) {
46
+ if (files.guardrails !== undefined) {
47
+ mismatch(`${LOCK_FILE} pins no guardrails file (the registry had none at pull), and guardrails were given: pull them with \`avaccess pull\`, or leave them out`);
48
+ }
49
+ return;
50
+ }
51
+ if (files.guardrails === undefined) {
52
+ mismatch(`${LOCK_FILE} pins the org's guardrails (${lock.guardrails.path}, ${lock.guardrails.hash}), and none were given: pass the pulled file as \`guardrails\`, so the org's denies apply`);
53
+ }
54
+ holdTo(lock.guardrails, files.guardrails, guardrailsHash);
55
+ };
56
+ /**
57
+ * The namespaces whose Resources a policies file reads as foreign, sorted: those
58
+ * whose beacons a service started from a lock checks (`checkBeacons`).
59
+ */
60
+ export const foreignOwnersOf = (policies) => [
61
+ ...new Set(Object.values(policies.schema).flatMap((facts) => facts.foreign === undefined ? [] : [facts.foreign])),
62
+ ].sort();
@@ -0,0 +1,13 @@
1
+ import type { AccessEngine, DecisionObserver } from "../access.engine.js";
2
+ type GovernedShape = {
3
+ readonly [resource: string]: readonly string[] | {
4
+ readonly [field: string]: "read" | "write" | "both";
5
+ };
6
+ };
7
+ /**
8
+ * `engine` with the token gate before it: a principal without a token's grant is
9
+ * asked of `engine` as it is. `observe` (the audit log's hook) hears the gate's own
10
+ * refusals of `can`; the engine reports the rest itself.
11
+ */
12
+ export declare const agentGated: <Governed extends GovernedShape>(engine: AccessEngine<Governed>, observe: DecisionObserver | undefined) => AccessEngine<Governed>;
13
+ export {};
@@ -0,0 +1,75 @@
1
+ // The gate a token's principal passes before the rules: what its access token grants
2
+ // (`agent.grant.ts`) narrows what its user's grants allow, and never widens it.
3
+ //
4
+ // - An agent's token is for the MCP endpoint alone (RFC 8707): the principal it
5
+ // yields is refused every question asked under any origin but `agent`, so the
6
+ // token opens nothing through the protocol routes, `_access/can` or a server-side
7
+ // call.
8
+ // - A token addressed to the service itself (`audience`) is taken under every origin.
9
+ // - Either way, `aventara:read` admits the read questions (`can` and `scopeOf` of
10
+ // `read`, `fieldsOf` of `read`) and `aventara:write` the write ones (`create`,
11
+ // `update`, `delete`, and `fieldsOf` of `write`); a question its scopes do not
12
+ // admit is refused. One they admit is the user's own, answered by the rules as
13
+ // they answer the user under that origin: a token never acts beyond the user who
14
+ // consented, and the rules may narrow it further by origin.
15
+ //
16
+ // A refused `can` is a decision like any other: the audit log has it, with the reason
17
+ // `deny:mcp-audience` (the wrong origin) or `deny:mcp-scope` (a scope missing).
18
+ import { grantOf, MCP_READ_SCOPE, MCP_WRITE_SCOPE, } from "./agent.grant.js";
19
+ /** The audit's names for the gate's two refusals. */
20
+ const MCP_AUDIENCE_RULE = "mcp-audience";
21
+ const MCP_SCOPE_RULE = "mcp-scope";
22
+ const NO_FIELDS = Object.freeze({
23
+ always: Object.freeze([]),
24
+ perRow: Object.freeze({}),
25
+ });
26
+ /** Why the gate refuses a question of a token's principal, or `undefined` when it passes it to the rules. */
27
+ const refusalOf = (grant, origin, writes) => {
28
+ if (grant.via === "mcp" && origin !== "agent") {
29
+ return MCP_AUDIENCE_RULE;
30
+ }
31
+ return grant.scopes.includes(writes ? MCP_WRITE_SCOPE : MCP_READ_SCOPE)
32
+ ? undefined
33
+ : MCP_SCOPE_RULE;
34
+ };
35
+ /**
36
+ * `engine` with the token gate before it: a principal without a token's grant is
37
+ * asked of `engine` as it is. `observe` (the audit log's hook) hears the gate's own
38
+ * refusals of `can`; the engine reports the rest itself.
39
+ */
40
+ export const agentGated = (engine, observe) => ({
41
+ // The startup facts, `explain` and the beacon check, as the engine has them.
42
+ ...engine,
43
+ can(question) {
44
+ const grant = grantOf(question.principal);
45
+ if (grant === undefined) {
46
+ return engine.can(question);
47
+ }
48
+ const refused = refusalOf(grant, question.origin, question.action !== "read");
49
+ if (refused === undefined) {
50
+ return engine.can(question);
51
+ }
52
+ if (observe === undefined) {
53
+ return false;
54
+ }
55
+ return Promise.resolve(observe(question, false, { kind: "deny", id: refused })).then(() => false);
56
+ },
57
+ scopeOf(question) {
58
+ const grant = grantOf(question.principal);
59
+ if (grant !== undefined &&
60
+ refusalOf(grant, question.origin, question.action !== "read") !==
61
+ undefined) {
62
+ return false;
63
+ }
64
+ return engine.scopeOf(question);
65
+ },
66
+ fieldsOf(question) {
67
+ const grant = grantOf(question.principal);
68
+ if (grant !== undefined &&
69
+ refusalOf(grant, question.origin, question.access === "write") !==
70
+ undefined) {
71
+ return NO_FIELDS;
72
+ }
73
+ return engine.fieldsOf(question);
74
+ },
75
+ });
@@ -0,0 +1,40 @@
1
+ import type { TokenGrant } from "../oauth/access-token.verifier.js";
2
+ /** The scope that lets an agent read: every `read` question its user's grants answer. */
3
+ export declare const MCP_READ_SCOPE = "aventara:read";
4
+ /** The scope that lets an agent write: every `create`, `update` and `delete` question its user's grants answer. */
5
+ export declare const MCP_WRITE_SCOPE = "aventara:write";
6
+ /** Whether a token's scopes name one of Access's: what a token addressed to the service must carry. */
7
+ export declare const hasAventaraScope: (scopes: ReadonlySet<string>) => boolean;
8
+ /** What an access token grants the principal it yields: its client, its scopes, and which resource it was for. */
9
+ export type CarriedGrant = TokenGrant & {
10
+ /** `mcp`: an agent's token for the MCP endpoint. `audience`: a token addressed to the service itself. */
11
+ readonly via: "mcp" | "audience";
12
+ };
13
+ /** A grant as the principal carries it: frozen, its scopes a frozen list (a `Set` stays writable when frozen). */
14
+ export type MarkedGrant = Omit<CarriedGrant, "scopes"> & {
15
+ readonly scopes: readonly string[];
16
+ };
17
+ /** Who acts for the user when a principal is an agent's: what `agentOf(me)` answers. */
18
+ export type AccessAgent = {
19
+ /** The OAuth client the agent's token was issued to. */
20
+ readonly clientId: string;
21
+ /** The token's scopes. */
22
+ readonly scopes: readonly string[];
23
+ };
24
+ /**
25
+ * The principal a token's request runs as: a copy of `me` (its own enumerable
26
+ * fields, on the same prototype) marked with the grant, by a non-enumerable property,
27
+ * so a spread, `JSON.stringify` or `Object.keys` of it does not show the mark. `null`
28
+ * stays `null` (an anonymous caller). A principal that is not an object cannot carry
29
+ * the grant, and is refused: such a request never runs as an unmarked principal.
30
+ */
31
+ export declare const withGrant: (me: unknown, grant: CarriedGrant) => unknown;
32
+ /** What a token grants the principal it yields; `undefined` for any other principal. */
33
+ export declare const grantOf: (principal: unknown) => MarkedGrant | undefined;
34
+ /**
35
+ * Whether `me` is an agent's: the principal a request with an agent's access token for
36
+ * the MCP endpoint runs as (`ctx.$state.me`), and the identity `principal` is given
37
+ * for one. Answers the client the token was issued to and its scopes; `null` for any
38
+ * other principal (a user's session, a token addressed to the service, nobody).
39
+ */
40
+ export declare const agentOf: (me: unknown) => AccessAgent | null;
@@ -0,0 +1,92 @@
1
+ // What an OAuth access token grants the caller it yields, carried on the principal
2
+ // its request runs as. The framework hands the policy engine `ctx.$state.me` and the
3
+ // request's origin, nothing else, so the grant travels with `me`, as the `sub` does
4
+ // (`principal.subject.ts`): under a key every copy of Access names the same
5
+ // (`Symbol.for`), so whichever instance the host asks for the policy (under Next,
6
+ // another bundle's) reads it.
7
+ //
8
+ // Two tokens carry one: an agent's, for the MCP endpoint (`via: "mcp"`), and one
9
+ // addressed to the service itself (`via: "audience"`, the opt-in). The policy holds
10
+ // either to its scopes before the rules (`agent-scope.gate.ts`). An agent's is public
11
+ // too: `agentOf(me)` tells app code that a principal is an agent's, and which client
12
+ // acts with which scopes.
13
+ //
14
+ // The principal a token's request runs as is a copy of the one `principal` returned:
15
+ // an app may hand out one object per user (cached), and the grant marks this
16
+ // request's object only, never the user's own requests that share it.
17
+ import { AccessError } from "../access/access.error.js";
18
+ /** The scope that lets an agent read: every `read` question its user's grants answer. */
19
+ export const MCP_READ_SCOPE = "aventara:read";
20
+ /** The scope that lets an agent write: every `create`, `update` and `delete` question its user's grants answer. */
21
+ export const MCP_WRITE_SCOPE = "aventara:write";
22
+ /** Whether a token's scopes name one of Access's: what a token addressed to the service must carry. */
23
+ export const hasAventaraScope = (scopes) => scopes.has(MCP_READ_SCOPE) || scopes.has(MCP_WRITE_SCOPE);
24
+ /** The key the grant is carried under: the same in every bundle and every copy of Access. */
25
+ const GRANT = Symbol.for("@aventara/access:agent-grant");
26
+ /** The key of the one map, per process, holding the grant of a principal that takes no property. */
27
+ const UNMARKABLE = Symbol.for("@aventara/access:agent-grants");
28
+ const unmarkable = () => {
29
+ const holder = globalThis;
30
+ holder[UNMARKABLE] ??= new WeakMap();
31
+ return holder[UNMARKABLE];
32
+ };
33
+ /**
34
+ * The principal a token's request runs as: a copy of `me` (its own enumerable
35
+ * fields, on the same prototype) marked with the grant, by a non-enumerable property,
36
+ * so a spread, `JSON.stringify` or `Object.keys` of it does not show the mark. `null`
37
+ * stays `null` (an anonymous caller). A principal that is not an object cannot carry
38
+ * the grant, and is refused: such a request never runs as an unmarked principal.
39
+ */
40
+ export const withGrant = (me, grant) => {
41
+ if (me === null || me === undefined) {
42
+ return null;
43
+ }
44
+ if (typeof me !== "object") {
45
+ throw new AccessError("ACCESS_OPTIONS_INVALID", "`principal` returned a value that is not an object for a request with an OAuth access token: Access marks the principal it runs as with what its token grants, so the policies hold it to its scopes, and only an object carries the mark. Return an object (or `null`).");
46
+ }
47
+ const copy = Object.assign(Object.create(Object.getPrototypeOf(me)), me);
48
+ const frozen = Object.freeze({
49
+ clientId: grant.clientId,
50
+ scopes: Object.freeze([...grant.scopes]),
51
+ via: grant.via,
52
+ });
53
+ try {
54
+ Object.defineProperty(copy, GRANT, {
55
+ value: frozen,
56
+ enumerable: false,
57
+ writable: false,
58
+ configurable: false,
59
+ });
60
+ }
61
+ catch {
62
+ unmarkable().set(copy, frozen);
63
+ }
64
+ return copy;
65
+ };
66
+ /** What a token grants the principal it yields; `undefined` for any other principal. */
67
+ export const grantOf = (principal) => {
68
+ if (typeof principal !== "object" || principal === null) {
69
+ return undefined;
70
+ }
71
+ const carried = Reflect.get(principal, GRANT);
72
+ if (carried !== undefined) {
73
+ return carried;
74
+ }
75
+ return unmarkable().get(principal);
76
+ };
77
+ /**
78
+ * Whether `me` is an agent's: the principal a request with an agent's access token for
79
+ * the MCP endpoint runs as (`ctx.$state.me`), and the identity `principal` is given
80
+ * for one. Answers the client the token was issued to and its scopes; `null` for any
81
+ * other principal (a user's session, a token addressed to the service, nobody).
82
+ */
83
+ export const agentOf = (me) => {
84
+ const grant = grantOf(me);
85
+ if (grant === undefined || grant.via !== "mcp") {
86
+ return null;
87
+ }
88
+ return Object.freeze({
89
+ clientId: grant.clientId,
90
+ scopes: Object.freeze([...grant.scopes]),
91
+ });
92
+ };
@@ -0,0 +1,60 @@
1
+ /** The `mcp` block. */
2
+ export type AccessMcpOptions = {
3
+ /**
4
+ * The MCP endpoint's URL, as its protected-resource metadata names it (RFC 9728
5
+ * `resource`): `https://app.example.com/mcp`. Every access token an agent is
6
+ * issued for it carries it as the audience (RFC 8707), and `access.mcp.verify`
7
+ * takes no token that does not. List it in the OAuth provider's `resources` too.
8
+ */
9
+ readonly resource: string;
10
+ /**
11
+ * Takes the OAuth provider's open dynamic client registration
12
+ * (`allowUnauthenticatedClientRegistration: true`), which Access refuses otherwise:
13
+ * a client registered that way is whoever it says it is, by its name, its logo and
14
+ * its redirect URI. Prefer Client ID Metadata Documents (`@better-auth/cimd`), whose
15
+ * client is the domain its document is served from. Off by default.
16
+ */
17
+ readonly openRegistration?: boolean;
18
+ };
19
+ /** `access.mcp`: the framework's `mcp.authorization`, `mcp: { path, authorization: access.mcp }`. */
20
+ export type AccessMcpAuthorization<Claims> = {
21
+ /** The authorization server an agent's token comes from: Access's issuer, alone. */
22
+ readonly servers: readonly [string];
23
+ /**
24
+ * Checks one bearer token: an access token Better Auth's OAuth provider issued
25
+ * for the MCP endpoint, current, of a client that is not disabled, a user who
26
+ * still exists and still consents to its scopes and the endpoint, and the session it
27
+ * was issued in, which has not ended. Resolves to who the agent acts for, with the claims
28
+ * that user holds now, the client and the scopes; `null` for any other token.
29
+ */
30
+ verify(token: string): Promise<AccessAgentIdentity<Claims> | null>;
31
+ };
32
+ /** Who an agent's access token acts for, as `access.mcp.verify` resolves it. */
33
+ export type AccessAgentIdentity<Claims> = {
34
+ /** The user the agent acts for. */
35
+ readonly sub: string;
36
+ /** The claims that user holds now, as a token `GET /token` issued now would carry them. */
37
+ readonly claims: Claims;
38
+ /** The OAuth client the token was issued to. */
39
+ readonly clientId: string;
40
+ /** The token's scopes. */
41
+ readonly scopes: readonly string[];
42
+ };
43
+ /** The checked `mcp` block. */
44
+ type CheckedMcp = {
45
+ readonly resource: string;
46
+ readonly openRegistration: boolean;
47
+ };
48
+ /**
49
+ * The `mcp` block, checked: `{ resource, openRegistration? }`, the MCP endpoint's URL
50
+ * as its protected-resource metadata writes it (`checkResourceUrl`), and whether open
51
+ * registration is taken. `undefined` when the block is absent.
52
+ */
53
+ export declare const checkMcpOptions: (value: unknown, at: string, issuer: string) => CheckedMcp | undefined;
54
+ /**
55
+ * The authorization server `access.mcp.servers` names: the issuer, when it is https,
56
+ * or http on `127.0.0.1` or `[::1]` (the framework's rule). `localhost` is
57
+ * refused with the address to write instead.
58
+ */
59
+ export declare const authorizationServerOf: (issuer: string, baseURLAt: string) => string;
60
+ export {};