@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,13 @@
1
+ // The registry's keys. Each namespace has one push key and one read-only pull key.
2
+ // A key is 32 random bytes, written `avk_push_…` or `avk_pull_…` so a secret scanner
3
+ // and a reader both see what it is. The registry keeps only its SHA-256 hash: the key
4
+ // itself is in the answer that issued it and nowhere else, never in a table, a log
5
+ // line or a later response.
6
+ import { randomSecret } from "../random-secret.generator.js";
7
+ import { sha256Hex } from "../sha256.digest.js";
8
+ export const KEY_KINDS = ["push", "pull"];
9
+ const KEY = /^avk_(push|pull)_[A-Za-z0-9_-]{43}$/;
10
+ /** A new key of `kind`: 256 random bits from the runtime's CSPRNG. */
11
+ export const newKey = (kind) => randomSecret(`avk_${kind}_`);
12
+ /** The hash the registry stores and looks a key up by; `undefined` for text that is no key. */
13
+ export const keyHashOf = (key) => KEY.test(key) ? `sha256:${sha256Hex(key)}` : undefined;
@@ -0,0 +1,13 @@
1
+ import type { RegistrySettings } from "./access.registry.js";
2
+ /** The `registry` block: `{ enabled: true }` turns the registry on; `{ enabled: false }` is the default. */
3
+ export type AccessRegistryOptions<E extends boolean = boolean> = {
4
+ readonly enabled: E;
5
+ /**
6
+ * Who gives a Resource its owner: `"first-push"` (the default), the first push
7
+ * that names it as its own unless the operator assigned it already
8
+ * (`access.registry.assignResource`), or `"assigned"`, the operator alone.
9
+ */
10
+ readonly ownership?: "first-push" | "assigned";
11
+ };
12
+ /** The registry's settings when the block turns it on; `undefined` when it is off. */
13
+ export declare const registryOptionsOf: (registry: unknown) => RegistrySettings | undefined;
@@ -0,0 +1,28 @@
1
+ // The `registry` block of `createAccess` and `aventaraAccess()`, checked: `{ enabled: true }` turns the
2
+ // registry on, with `ownership` saying who gives a Resource its owner
3
+ // (`"first-push"`, the default, or `"assigned"`); `{ enabled: false }`, the default,
4
+ // leaves it off.
5
+ import { AccessError } from "../access/access.error.js";
6
+ import { isRecord } from "../policies.compiler.js";
7
+ const OWNERSHIP = ["first-push", "assigned"];
8
+ const KEYS = ["enabled", "ownership"];
9
+ /** The registry's settings when the block turns it on; `undefined` when it is off. */
10
+ export const registryOptionsOf = (registry) => {
11
+ if (registry === undefined) {
12
+ return undefined;
13
+ }
14
+ if (!isRecord(registry) ||
15
+ typeof registry.enabled !== "boolean" ||
16
+ Object.keys(registry).some((key) => !KEYS.includes(key)) ||
17
+ (registry.ownership !== undefined &&
18
+ !OWNERSHIP.includes(registry.ownership)) ||
19
+ (registry.enabled === false && registry.ownership !== undefined)) {
20
+ throw new AccessError("ACCESS_OPTIONS_INVALID", '`registry` is `{ enabled: true }` (the policy registry: its tables and its routes), optionally with `ownership: "first-push"` (the default) or `"assigned"`, or `{ enabled: false }`, the default.');
21
+ }
22
+ return registry.enabled
23
+ ? {
24
+ ownership: registry.ownership ??
25
+ "first-push",
26
+ }
27
+ : undefined;
28
+ };
@@ -0,0 +1,6 @@
1
+ import { AccessError, type AccessErrorCode } from "../access/access.error.js";
2
+ /** A refusal over HTTP: the error, and the status it is answered with. */
3
+ export declare class AccessRegistryRefusal extends AccessError {
4
+ readonly status: number;
5
+ constructor(code: AccessErrorCode, message: string, status: number);
6
+ }
@@ -0,0 +1,11 @@
1
+ // A registry refusal over HTTP: the `AccessError` a registry call throws, with the
2
+ // status its route answers it with.
3
+ import { AccessError } from "../access/access.error.js";
4
+ /** A refusal over HTTP: the error, and the status it is answered with. */
5
+ export class AccessRegistryRefusal extends AccessError {
6
+ status;
7
+ constructor(code, message, status) {
8
+ super(code, message);
9
+ this.status = status;
10
+ }
11
+ }
@@ -0,0 +1,238 @@
1
+ /** The models the registry declares, by Better Auth model name. */
2
+ export declare const REGISTRY_MODELS: {
3
+ readonly namespace: "accessNamespace";
4
+ readonly key: "accessKey";
5
+ readonly policyRevision: "accessPolicyRevision";
6
+ readonly guardrailRevision: "accessGuardrailRevision";
7
+ readonly head: "accessHead";
8
+ readonly pushLog: "accessPushLog";
9
+ readonly owner: "accessResourceOwner";
10
+ readonly beacon: "accessBeacon";
11
+ };
12
+ /** The registry's tables, in Better Auth's plugin schema form. */
13
+ export declare const registrySchema: {
14
+ readonly accessNamespace: {
15
+ readonly fields: {
16
+ readonly name: {
17
+ readonly type: "string";
18
+ readonly required: true;
19
+ readonly unique: true;
20
+ };
21
+ readonly createdAt: {
22
+ readonly type: "date";
23
+ readonly required: true;
24
+ readonly input: false;
25
+ readonly defaultValue: () => Date;
26
+ };
27
+ };
28
+ };
29
+ readonly accessKey: {
30
+ readonly fields: {
31
+ readonly namespaceId: {
32
+ readonly type: "string";
33
+ readonly required: true;
34
+ readonly index: true;
35
+ readonly references: {
36
+ readonly model: "accessNamespace";
37
+ readonly field: "id";
38
+ readonly onDelete: "restrict";
39
+ };
40
+ };
41
+ readonly kind: {
42
+ readonly type: "string";
43
+ readonly required: true;
44
+ };
45
+ readonly keyHash: {
46
+ readonly type: "string";
47
+ readonly required: true;
48
+ readonly unique: true;
49
+ };
50
+ readonly slot: {
51
+ readonly type: "string";
52
+ readonly required: false;
53
+ readonly unique: true;
54
+ };
55
+ readonly createdAt: {
56
+ readonly type: "date";
57
+ readonly required: true;
58
+ readonly input: false;
59
+ readonly defaultValue: () => Date;
60
+ };
61
+ readonly revokedAt: {
62
+ readonly type: "date";
63
+ readonly required: false;
64
+ };
65
+ };
66
+ };
67
+ readonly accessPolicyRevision: {
68
+ readonly fields: {
69
+ readonly hash: {
70
+ readonly type: "string";
71
+ readonly required: true;
72
+ readonly unique: true;
73
+ };
74
+ readonly format: {
75
+ readonly type: "string";
76
+ readonly required: true;
77
+ };
78
+ readonly file: {
79
+ readonly type: "string";
80
+ readonly required: true;
81
+ };
82
+ readonly createdAt: {
83
+ readonly type: "date";
84
+ readonly required: true;
85
+ readonly input: false;
86
+ readonly defaultValue: () => Date;
87
+ };
88
+ readonly namespaceId: {
89
+ readonly type: "string";
90
+ readonly required: true;
91
+ readonly index: true;
92
+ readonly references: {
93
+ readonly model: "accessNamespace";
94
+ readonly field: "id";
95
+ readonly onDelete: "restrict";
96
+ };
97
+ };
98
+ };
99
+ };
100
+ readonly accessGuardrailRevision: {
101
+ readonly fields: {
102
+ readonly hash: {
103
+ readonly type: "string";
104
+ readonly required: true;
105
+ readonly unique: true;
106
+ };
107
+ readonly format: {
108
+ readonly type: "string";
109
+ readonly required: true;
110
+ };
111
+ readonly file: {
112
+ readonly type: "string";
113
+ readonly required: true;
114
+ };
115
+ readonly createdAt: {
116
+ readonly type: "date";
117
+ readonly required: true;
118
+ readonly input: false;
119
+ readonly defaultValue: () => Date;
120
+ };
121
+ };
122
+ };
123
+ readonly accessHead: {
124
+ readonly fields: {
125
+ readonly line: {
126
+ readonly type: "string";
127
+ readonly required: true;
128
+ readonly unique: true;
129
+ };
130
+ readonly hash: {
131
+ readonly type: "string";
132
+ readonly required: true;
133
+ };
134
+ readonly updatedAt: {
135
+ readonly type: "date";
136
+ readonly required: true;
137
+ };
138
+ };
139
+ };
140
+ readonly accessPushLog: {
141
+ readonly fields: {
142
+ readonly line: {
143
+ readonly type: "string";
144
+ readonly required: true;
145
+ readonly index: true;
146
+ };
147
+ readonly keyId: {
148
+ readonly type: "string";
149
+ readonly required: false;
150
+ };
151
+ readonly hash: {
152
+ readonly type: "string";
153
+ readonly required: false;
154
+ };
155
+ readonly outcome: {
156
+ readonly type: "string";
157
+ readonly required: true;
158
+ };
159
+ readonly code: {
160
+ readonly type: "string";
161
+ readonly required: false;
162
+ };
163
+ readonly count: {
164
+ readonly type: "number";
165
+ readonly required: true;
166
+ };
167
+ readonly createdAt: {
168
+ readonly type: "date";
169
+ readonly required: true;
170
+ readonly input: false;
171
+ readonly defaultValue: () => Date;
172
+ };
173
+ };
174
+ };
175
+ readonly accessBeacon: {
176
+ readonly fields: {
177
+ readonly namespaceId: {
178
+ readonly type: "string";
179
+ readonly required: true;
180
+ readonly index: true;
181
+ readonly references: {
182
+ readonly model: "accessNamespace";
183
+ readonly field: "id";
184
+ readonly onDelete: "restrict";
185
+ };
186
+ };
187
+ readonly beaconHash: {
188
+ readonly type: "string";
189
+ readonly required: true;
190
+ readonly unique: true;
191
+ };
192
+ readonly slot: {
193
+ readonly type: "string";
194
+ readonly required: false;
195
+ readonly unique: true;
196
+ };
197
+ readonly createdAt: {
198
+ readonly type: "date";
199
+ readonly required: true;
200
+ readonly input: false;
201
+ readonly defaultValue: () => Date;
202
+ };
203
+ readonly retiredAt: {
204
+ readonly type: "date";
205
+ readonly required: false;
206
+ };
207
+ };
208
+ };
209
+ readonly accessResourceOwner: {
210
+ readonly fields: {
211
+ readonly resource: {
212
+ readonly type: "string";
213
+ readonly required: true;
214
+ readonly unique: true;
215
+ };
216
+ readonly namespaceId: {
217
+ readonly type: "string";
218
+ readonly required: true;
219
+ readonly index: true;
220
+ readonly references: {
221
+ readonly model: "accessNamespace";
222
+ readonly field: "id";
223
+ readonly onDelete: "restrict";
224
+ };
225
+ };
226
+ readonly source: {
227
+ readonly type: "string";
228
+ readonly required: true;
229
+ };
230
+ readonly createdAt: {
231
+ readonly type: "date";
232
+ readonly required: true;
233
+ readonly input: false;
234
+ readonly defaultValue: () => Date;
235
+ };
236
+ };
237
+ };
238
+ };
@@ -0,0 +1,127 @@
1
+ // The registry's tables, declared through Better Auth's plugin schema: the Access
2
+ // plugin carries them when `registry: { enabled: true }`, so Better Auth's own
3
+ // migration (or its CLI's `generate` for an ORM's schema) creates them beside Better
4
+ // Auth's, and they are read and written through whatever database adapter the app
5
+ // gave Better Auth. Access names no ORM and writes no SQL.
6
+ //
7
+ // The names follow Better Auth's own plugins (`twoFactor`, `jwks`): camelCase models,
8
+ // which Better Auth's Prisma generator turns into `AccessNamespace` and so on.
9
+ //
10
+ // - `accessNamespace`: one row per namespace, written once.
11
+ // - `accessKey`: a namespace's push and pull keys, as SHA-256 hashes only. Rotating
12
+ // or revoking a key stamps `revokedAt` and clears its `slot`; a key's row is never
13
+ // deleted, so a push log entry keeps naming the key that made it. A live key holds
14
+ // its namespace's slot for its kind (`<namespace id>:push` or `:pull`), unique: two
15
+ // rotations at once cannot both leave a live key.
16
+ // - `accessResourceOwner`: which namespace owns each Resource, one row per Resource,
17
+ // its name unique: two pushes at once cannot both claim it. `source` says who
18
+ // made the row: `push` (the first push that named it, released when the
19
+ // namespace's head no longer does) or `operator` (`assignResource`, until
20
+ // `releaseResource`).
21
+ // - `accessPolicyRevision` and `accessGuardrailRevision`: the pushed files, keyed by
22
+ // their hash and stored in their canonical text, written once and never updated.
23
+ // - `accessHead`: the current revision of each line (a namespace's policies, or the
24
+ // org's guardrails).
25
+ // - `accessBeacon`: each namespace's beacons, as the SHA-256 hash of
26
+ // `<namespace>:<value>` its service wrote in its `_aventara_beacon` row and pushed
27
+ // with its file; the value itself is never stored. The current one holds its
28
+ // namespace's `slot` (unique); a push with another value retires it (`retiredAt`,
29
+ // no slot: the namespace moved to another database) and is the current one from
30
+ // then on. A retired hash never comes back.
31
+ // - `accessPushLog`: every push of a key the registry has, and every guardrails
32
+ // push, with its outcome; refusals of a key it does not have (or of none) add up
33
+ // in one row per namespace per minute, its `count`.
34
+ /** The models the registry declares, by Better Auth model name. */
35
+ export const REGISTRY_MODELS = {
36
+ namespace: "accessNamespace",
37
+ key: "accessKey",
38
+ policyRevision: "accessPolicyRevision",
39
+ guardrailRevision: "accessGuardrailRevision",
40
+ head: "accessHead",
41
+ pushLog: "accessPushLog",
42
+ owner: "accessResourceOwner",
43
+ beacon: "accessBeacon",
44
+ };
45
+ const createdAt = {
46
+ type: "date",
47
+ required: true,
48
+ input: false,
49
+ defaultValue: () => new Date(),
50
+ };
51
+ const namespaceReference = {
52
+ type: "string",
53
+ required: true,
54
+ index: true,
55
+ references: {
56
+ model: REGISTRY_MODELS.namespace,
57
+ field: "id",
58
+ onDelete: "restrict",
59
+ },
60
+ };
61
+ /** A revision table: the file in its canonical text, under its hash. */
62
+ const revisionFields = {
63
+ hash: { type: "string", required: true, unique: true },
64
+ format: { type: "string", required: true },
65
+ file: { type: "string", required: true },
66
+ createdAt,
67
+ };
68
+ /** The registry's tables, in Better Auth's plugin schema form. */
69
+ export const registrySchema = {
70
+ [REGISTRY_MODELS.namespace]: {
71
+ fields: {
72
+ name: { type: "string", required: true, unique: true },
73
+ createdAt,
74
+ },
75
+ },
76
+ [REGISTRY_MODELS.key]: {
77
+ fields: {
78
+ namespaceId: namespaceReference,
79
+ kind: { type: "string", required: true },
80
+ keyHash: { type: "string", required: true, unique: true },
81
+ slot: { type: "string", required: false, unique: true },
82
+ createdAt,
83
+ revokedAt: { type: "date", required: false },
84
+ },
85
+ },
86
+ [REGISTRY_MODELS.policyRevision]: {
87
+ fields: { namespaceId: namespaceReference, ...revisionFields },
88
+ },
89
+ [REGISTRY_MODELS.guardrailRevision]: {
90
+ fields: revisionFields,
91
+ },
92
+ [REGISTRY_MODELS.head]: {
93
+ fields: {
94
+ line: { type: "string", required: true, unique: true },
95
+ hash: { type: "string", required: true },
96
+ updatedAt: { type: "date", required: true },
97
+ },
98
+ },
99
+ [REGISTRY_MODELS.pushLog]: {
100
+ fields: {
101
+ line: { type: "string", required: true, index: true },
102
+ keyId: { type: "string", required: false },
103
+ hash: { type: "string", required: false },
104
+ outcome: { type: "string", required: true },
105
+ code: { type: "string", required: false },
106
+ count: { type: "number", required: true },
107
+ createdAt,
108
+ },
109
+ },
110
+ [REGISTRY_MODELS.beacon]: {
111
+ fields: {
112
+ namespaceId: namespaceReference,
113
+ beaconHash: { type: "string", required: true, unique: true },
114
+ slot: { type: "string", required: false, unique: true },
115
+ createdAt,
116
+ retiredAt: { type: "date", required: false },
117
+ },
118
+ },
119
+ [REGISTRY_MODELS.owner]: {
120
+ fields: {
121
+ resource: { type: "string", required: true, unique: true },
122
+ namespaceId: namespaceReference,
123
+ source: { type: "string", required: true },
124
+ createdAt,
125
+ },
126
+ },
127
+ };
@@ -0,0 +1,96 @@
1
+ /** A `resources` key: the Better Auth tables it opens as Resources of the app's rules. */
2
+ export type AuthResourceKey = "users" | "members";
3
+ /** What a write through Aventara may set on one opened table, by action. */
4
+ type WritableFields = {
5
+ /** Fields a create may set (and the row's identifier), or `undefined`: no create. */
6
+ readonly create?: readonly string[];
7
+ /** Fields an update may set, or `undefined`: no update. */
8
+ readonly update?: readonly string[];
9
+ /** Whether a delete goes through. */
10
+ readonly delete: boolean;
11
+ };
12
+ /** One table a `resources` key opens: reads go to the app's policies, writes are limited. */
13
+ type OpenedTable = {
14
+ readonly key: AuthResourceKey;
15
+ readonly writes: WritableFields;
16
+ /**
17
+ * Fields Access governs for writing (`"write"`): read as the rules grant, written
18
+ * only through a grant that names the field. A role the app's rules read is one.
19
+ */
20
+ readonly governed?: readonly string[];
21
+ /** Where the table comes from, when a plugin adds it: named in the refusal when it is missing. */
22
+ readonly plugin?: string;
23
+ };
24
+ /** The tables each key opens, by Better Auth model name. */
25
+ export declare const OPENED_TABLES: {
26
+ readonly [model: string]: OpenedTable;
27
+ };
28
+ /** The tables each key must find in the app's schema, by key. */
29
+ export declare const KEY_TABLES: {
30
+ readonly [K in AuthResourceKey]: readonly string[];
31
+ };
32
+ /**
33
+ * The secret columns of Better Auth's tables, by Better Auth model name: no grant
34
+ * opens them, for reading or writing, on any table, opened or not.
35
+ */
36
+ export declare const SECRET_COLUMNS: {
37
+ readonly [model: string]: readonly string[];
38
+ };
39
+ /** One Better Auth table as the app's schema names it. */
40
+ export type AuthTable = {
41
+ /** Better Auth's model name (`user`, `member`), before the app renames it. */
42
+ readonly model: string;
43
+ /** The table's name in Better Auth: its `modelName`, or the model name. */
44
+ readonly name: string;
45
+ /** The Resource the app's schema has for it; `undefined` when it has none. */
46
+ readonly resource: string | undefined;
47
+ /** Better Auth's field names, to the app's. */
48
+ readonly fields: ReadonlyMap<string, string>;
49
+ };
50
+ /** One field of a Better Auth table, as `getAuthTables(options)` states it. */
51
+ export type BetterAuthField = {
52
+ readonly fieldName?: string;
53
+ /** `false`: a user cannot set it (at sign-up, or with `/update-user`). Absent means they can. */
54
+ readonly input?: boolean;
55
+ };
56
+ /** Better Auth's tables as `getAuthTables(options)` states them. */
57
+ export type BetterAuthTables = {
58
+ readonly [model: string]: {
59
+ readonly modelName?: string;
60
+ readonly fields: {
61
+ readonly [field: string]: BetterAuthField;
62
+ };
63
+ };
64
+ };
65
+ /**
66
+ * How Access knows which Resource of the rules' schema is which of Better Auth's
67
+ * tables.
68
+ *
69
+ * - `prisma`: Better Auth runs on its Prisma adapter, which reads a table through
70
+ * the Prisma client's delegate named as the table (`modelName`, or the model's own
71
+ * name, plus `s` with `usePlural`). The Prisma model behind that delegate (the
72
+ * delegate is the model's name with its first letter lower-cased) is the table:
73
+ * an exact match, whatever the database names its tables.
74
+ * - `map`: any other adapter reads the database's tables by their own names, which
75
+ * a Prisma schema can give any model name (`@@map`), so the app names each table's
76
+ * Resource itself (`resources.authTables`), or `null` for none.
77
+ */
78
+ export type AuthTableNaming = {
79
+ readonly kind: "prisma";
80
+ readonly usePlural: boolean;
81
+ } | {
82
+ readonly kind: "map";
83
+ readonly map: {
84
+ readonly [model: string]: string | null;
85
+ };
86
+ };
87
+ /**
88
+ * Better Auth's tables, for the options it is built with (its plugins' tables and
89
+ * Access's own included), by Better Auth model name, each with the Resource (when
90
+ * the rules' schema has it, as `naming` says) and the field names the app's schema
91
+ * has for it (Better Auth's `fieldName`s).
92
+ */
93
+ export declare const authTablesOf: (tables: BetterAuthTables, schema: {
94
+ readonly [resource: string]: unknown;
95
+ }, naming: AuthTableNaming) => ReadonlyMap<string, AuthTable>;
96
+ export {};
@@ -0,0 +1,110 @@
1
+ // What Access knows about Better Auth's tables (better-auth 1.7.12 to 1.7.14, its plugins, and
2
+ // Access's own plugin tables): which `resources` key opens each one as a Resource of
3
+ // the app's rules, which of its columns are secrets no grant ever opens, and which
4
+ // fields a write through Aventara may set. The names here are Better Auth's own
5
+ // (its model and field names, before an app renames them); `authTablesOf` maps them
6
+ // to the Resource and field names the app's schema has.
7
+ //
8
+ // A table no key opens stays Better Auth's: Access denies every action on it. A
9
+ // write through Aventara is a plain database write: it skips Better Auth's hooks
10
+ // (`databaseHooks`, plugins' `after` hooks), its cookie cache and its secondary
11
+ // storage, so the writes Access lets through are the ones those do not guard:
12
+ // a user's role and an organization's memberships. A role is governed for writing,
13
+ // so only a grant that names it sets it.
14
+ /** The tables each key opens, by Better Auth model name. */
15
+ export const OPENED_TABLES = {
16
+ // The users: a user's role may be set; creating or deleting a user, and every
17
+ // other field (name, email, ban), stays with Better Auth's own API.
18
+ // The role is governed for writing: only a grant that names `User.role` sets it.
19
+ user: {
20
+ key: "users",
21
+ writes: { update: ["role"], delete: false },
22
+ governed: ["role"],
23
+ },
24
+ session: { key: "users", writes: { delete: false } },
25
+ account: { key: "users", writes: { delete: false } },
26
+ // The organization plugin's memberships: a membership is created or removed, and
27
+ // its role changed; the organization itself is read only. The role is governed
28
+ // for writing: only a grant that names it sets it, at create or update.
29
+ member: {
30
+ key: "members",
31
+ plugin: "organization()",
32
+ writes: {
33
+ create: ["organizationId", "userId", "role", "createdAt"],
34
+ update: ["role"],
35
+ delete: true,
36
+ },
37
+ governed: ["role"],
38
+ },
39
+ organization: {
40
+ key: "members",
41
+ plugin: "organization()",
42
+ writes: { delete: false },
43
+ },
44
+ };
45
+ /** The tables each key must find in the app's schema, by key. */
46
+ export const KEY_TABLES = {
47
+ users: ["user", "session", "account"],
48
+ members: ["member", "organization"],
49
+ };
50
+ /**
51
+ * The secret columns of Better Auth's tables, by Better Auth model name: no grant
52
+ * opens them, for reading or writing, on any table, opened or not.
53
+ */
54
+ export const SECRET_COLUMNS = {
55
+ // The password hash, and the provider's tokens for social sign-in.
56
+ account: ["password", "accessToken", "refreshToken", "idToken"],
57
+ // The session token: a bearer of it is signed in.
58
+ session: ["token"],
59
+ // Reset, verification, magic-link and OTP values; the identifier carries the
60
+ // reset token itself (`reset-password:<token>`).
61
+ verification: ["identifier", "value"],
62
+ // The JWT signing key (`jwt()`, which createAccess installs).
63
+ jwks: ["privateKey"],
64
+ // `twoFactor()`: the TOTP secret and the backup codes.
65
+ twoFactor: ["secret", "backupCodes"],
66
+ // `deviceAuthorization()`: the codes a device signs in with.
67
+ deviceCode: ["deviceCode", "userCode"],
68
+ // Access's registry (`registry: { enabled: true }`): a namespace key's hash.
69
+ accessKey: ["keyHash"],
70
+ // The Access server's ownership beacon: its value proves a reader reaches this
71
+ // database, so no grant ever reads it.
72
+ aventaraBeacon: ["value"],
73
+ // The kiosk profile: a device credential's hashes, and a PIN's.
74
+ accessKioskDevice: ["secretHash", "previousSecretHash"],
75
+ accessKioskPin: ["pinHash"],
76
+ accessKioskRetiredSecret: ["secretHash"],
77
+ };
78
+ /** The Prisma client's delegate for a model: its name with the first letter lower-cased. */
79
+ const delegateOf = (model) => `${model.charAt(0).toLowerCase()}${model.slice(1)}`;
80
+ /**
81
+ * Better Auth's tables, for the options it is built with (its plugins' tables and
82
+ * Access's own included), by Better Auth model name, each with the Resource (when
83
+ * the rules' schema has it, as `naming` says) and the field names the app's schema
84
+ * has for it (Better Auth's `fieldName`s).
85
+ */
86
+ export const authTablesOf = (tables, schema, naming) => {
87
+ const resources = Object.keys(schema);
88
+ const found = new Map();
89
+ for (const model of Object.keys(tables)) {
90
+ const name = tables[model]?.modelName ?? model;
91
+ let resource;
92
+ if (naming.kind === "map") {
93
+ resource = naming.map[model] ?? undefined;
94
+ }
95
+ else {
96
+ const delegate = naming.usePlural ? `${name}s` : name;
97
+ for (const candidate of resources) {
98
+ if (delegateOf(candidate) === delegate) {
99
+ resource = candidate;
100
+ }
101
+ }
102
+ }
103
+ const fields = new Map();
104
+ for (const [field, attribute] of Object.entries(tables[model]?.fields ?? {})) {
105
+ fields.set(field, attribute.fieldName ?? field);
106
+ }
107
+ found.set(model, { model, name, resource, fields });
108
+ }
109
+ return found;
110
+ };