@fuzdev/fuz_app 0.107.0 → 0.109.0

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 (95) hide show
  1. package/README.md +4 -0
  2. package/dist/actions/CLAUDE.md +1 -1
  3. package/dist/auth/CLAUDE.md +1 -1
  4. package/dist/auth/account_route_schema.d.ts +10 -1
  5. package/dist/auth/account_route_schema.d.ts.map +1 -1
  6. package/dist/auth/account_route_schema.js +10 -1
  7. package/dist/auth/account_routes.d.ts +18 -4
  8. package/dist/auth/account_routes.d.ts.map +1 -1
  9. package/dist/auth/account_routes.js +32 -29
  10. package/dist/auth/api_token_queries.d.ts +4 -0
  11. package/dist/auth/api_token_queries.d.ts.map +1 -1
  12. package/dist/auth/api_token_queries.js +5 -1
  13. package/dist/auth/audit_emitter.d.ts +9 -5
  14. package/dist/auth/audit_emitter.d.ts.map +1 -1
  15. package/dist/auth/audit_log_schema.d.ts +7 -1
  16. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  17. package/dist/auth/audit_log_schema.js +16 -1
  18. package/dist/auth/bearer_auth.d.ts +9 -6
  19. package/dist/auth/bearer_auth.d.ts.map +1 -1
  20. package/dist/auth/bearer_auth.js +12 -21
  21. package/dist/auth/bootstrap_routes.d.ts +12 -2
  22. package/dist/auth/bootstrap_routes.d.ts.map +1 -1
  23. package/dist/auth/bootstrap_routes.js +26 -10
  24. package/dist/auth/middleware.d.ts +0 -3
  25. package/dist/auth/middleware.d.ts.map +1 -1
  26. package/dist/auth/middleware.js +11 -10
  27. package/dist/auth/migrations.d.ts +7 -0
  28. package/dist/auth/migrations.d.ts.map +1 -1
  29. package/dist/auth/migrations.js +45 -4
  30. package/dist/auth/session_queries.d.ts +13 -2
  31. package/dist/auth/session_queries.d.ts.map +1 -1
  32. package/dist/auth/session_queries.js +14 -3
  33. package/dist/auth/signup_routes.d.ts +10 -0
  34. package/dist/auth/signup_routes.d.ts.map +1 -1
  35. package/dist/auth/signup_routes.js +14 -11
  36. package/dist/db/CLAUDE.md +9 -5
  37. package/dist/db/cell_audit_queries.d.ts +4 -2
  38. package/dist/db/cell_audit_queries.d.ts.map +1 -1
  39. package/dist/db/cell_audit_queries.js +4 -2
  40. package/dist/db/pg_error.d.ts +12 -0
  41. package/dist/db/pg_error.d.ts.map +1 -1
  42. package/dist/db/pg_error.js +15 -1
  43. package/dist/http/CLAUDE.md +78 -18
  44. package/dist/http/db_routes.d.ts +96 -3
  45. package/dist/http/db_routes.d.ts.map +1 -1
  46. package/dist/http/db_routes.js +263 -46
  47. package/dist/http/error_schemas.d.ts +13 -2
  48. package/dist/http/error_schemas.d.ts.map +1 -1
  49. package/dist/http/error_schemas.js +13 -2
  50. package/dist/http/pending_effects.d.ts +5 -5
  51. package/dist/http/pending_effects.d.ts.map +1 -1
  52. package/dist/http/pending_effects.js +1 -1
  53. package/dist/rate_limiter.d.ts +36 -3
  54. package/dist/rate_limiter.d.ts.map +1 -1
  55. package/dist/rate_limiter.js +36 -3
  56. package/dist/server/app_server.d.ts +27 -10
  57. package/dist/server/app_server.d.ts.map +1 -1
  58. package/dist/server/app_server.js +30 -20
  59. package/dist/server/app_server_context.d.ts +10 -2
  60. package/dist/server/app_server_context.d.ts.map +1 -1
  61. package/dist/testing/CLAUDE.md +42 -8
  62. package/dist/testing/app_server.d.ts.map +1 -1
  63. package/dist/testing/app_server.js +6 -4
  64. package/dist/testing/audit_completeness.d.ts.map +1 -1
  65. package/dist/testing/audit_completeness.js +5 -1
  66. package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -1
  67. package/dist/testing/cross_backend/cell_relations.js +53 -1
  68. package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -1
  69. package/dist/testing/cross_backend/default_spine_surface.js +9 -8
  70. package/dist/testing/cross_backend/login_security.d.ts.map +1 -1
  71. package/dist/testing/cross_backend/login_security.js +59 -0
  72. package/dist/testing/cross_backend/query_shape.d.ts +10 -0
  73. package/dist/testing/cross_backend/query_shape.d.ts.map +1 -0
  74. package/dist/testing/cross_backend/query_shape.js +128 -0
  75. package/dist/testing/cross_backend/session_cap.d.ts +19 -0
  76. package/dist/testing/cross_backend/session_cap.d.ts.map +1 -0
  77. package/dist/testing/cross_backend/session_cap.js +93 -0
  78. package/dist/testing/middleware.d.ts +2 -5
  79. package/dist/testing/middleware.d.ts.map +1 -1
  80. package/dist/testing/middleware.js +5 -5
  81. package/dist/testing/rate_limiting.d.ts +10 -16
  82. package/dist/testing/rate_limiting.d.ts.map +1 -1
  83. package/dist/testing/rate_limiting.js +11 -95
  84. package/dist/testing/stubs.d.ts.map +1 -1
  85. package/dist/testing/stubs.js +3 -1
  86. package/dist/testing/testing_rate_limiter.d.ts +7 -1
  87. package/dist/testing/testing_rate_limiter.d.ts.map +1 -1
  88. package/dist/testing/testing_rate_limiter.js +7 -1
  89. package/dist/ui/CLAUDE.md +9 -4
  90. package/dist/ui/table_state.svelte.d.ts +18 -5
  91. package/dist/ui/table_state.svelte.d.ts.map +1 -1
  92. package/dist/ui/table_state.svelte.js +22 -7
  93. package/dist/ui/ui_format.d.ts.map +1 -1
  94. package/dist/ui/ui_format.js +4 -0
  95. package/package.json +1 -1
package/README.md CHANGED
@@ -45,6 +45,10 @@ Deno.serve({port: PORT, hostname: HOST}, app.fetch);
45
45
 
46
46
  See [CLAUDE.md](CLAUDE.md) for more usage patterns and the AI-generated docs.
47
47
 
48
+ ## Contributing
49
+
50
+ [fuz.dev/contributing](https://www.fuz.dev/contributing)
51
+
48
52
  ## License
49
53
 
50
54
  [MIT](LICENSE)
@@ -217,7 +217,7 @@ ones. Matches the Rust spine, which validates handler-side.
217
217
 
218
218
  Shim responsibilities (per-request):
219
219
 
220
- 1. Parse envelope (POST body / GET query string); parse errors → `parse_error` 400.
220
+ 1. Parse envelope (POST body / GET query string); parse errors → `parse_error` 400. GET reads `method` / `id` / `params` via Hono's `c.req.query()` — first occurrence wins on a duplicated key, unknown keys are ignored, and an empty value counts as missing. This is the cross-impl contract (the Rust GET dispatch mirrors it via `fuz_http`'s `query_first`, including the id numeric-vs-string normalization), pinned by the `query_shape` cross suite.
221
221
  2. Lookup method in the `compile_action_registry`-built map; unknown → `method_not_found`.
222
222
  3. GET read restriction — GET rejected for `side_effects: true` actions.
223
223
  4. Build `PerformActionInput` from `c.var` + `get_client_ip` + `c.req.raw.signal`. Test-preset escape hatch reads `TEST_CONTEXT_PRESET_KEY` + `REQUEST_CONTEXT_KEY`.
@@ -379,7 +379,7 @@ token_create role_grant_offer_supersede
379
379
  token_revoke invite_create
380
380
  token_revoke_all invite_delete
381
381
  account_delete app_settings_update
382
- account_purge
382
+ account_purge db_admin_row_delete
383
383
  account_undelete
384
384
  actor_delete
385
385
  actor_purge
@@ -79,7 +79,16 @@ export declare const PasswordChangeOutput: z.ZodObject<{
79
79
  tokens_revoked: z.ZodNumber;
80
80
  }, z.core.$strict>;
81
81
  export type PasswordChangeOutput = z.infer<typeof PasswordChangeOutput>;
82
- /** Default maximum sessions per account. */
82
+ /**
83
+ * Default maximum sessions per account.
84
+ *
85
+ * The Rust twin pins the same 5 as a `const` (`fuz_auth`'s
86
+ * `DEFAULT_MAX_SESSIONS`) rather than a route-state field, because its
87
+ * `AccountRouteState` has no `Default` and a new field would break every
88
+ * exhaustive literal in every consumer workspace. So this stays the
89
+ * configurable side of the pair (`null` disables), and the two ship the same
90
+ * number — retuning means editing both.
91
+ */
83
92
  export declare const DEFAULT_MAX_SESSIONS = 5;
84
93
  /** Default maximum API tokens per account. */
85
94
  export declare const DEFAULT_MAX_TOKENS = 10;
@@ -1 +1 @@
1
- {"version":3,"file":"account_route_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_route_schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAQvD,kFAAkF;AAClF,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAC3C,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,4CAA4C;AAC5C,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;kBAI9B,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEtE,4EAA4E;AAC5E,eAAO,MAAM,iCAAiC;;;iBAG5C,CAAC;AACH,MAAM,MAAM,iCAAiC,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAElG,oFAAoF;AACpF,eAAO,MAAM,UAAU;;;kBAGrB,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD,6EAA6E;AAC7E,eAAO,MAAM,WAAW;;kBAEtB,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD,2EAA2E;AAC3E,eAAO,MAAM,WAAW,WAAW,CAAC;AACpC,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD,wFAAwF;AACxF,eAAO,MAAM,YAAY;;;kBAGvB,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAExD,sHAAsH;AACtH,eAAO,MAAM,mBAAmB;;;kBAG9B,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEtE,uGAAuG;AACvG,eAAO,MAAM,oBAAoB;;;;kBAI/B,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE,4CAA4C;AAC5C,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,8CAA8C;AAC9C,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAUD,CAAC;AAEvC,8EAA8E;AAC9E,MAAM,WAAW,wBAAwB;IACxC,8FAA8F;IAC9F,0BAA0B,EAAE,OAAO,CAAC;CACpC;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,GACvC,SAAS,wBAAwB,KAC/B,CACF,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,EAC1B,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,EAC1B,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,EAC1B,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAoD1B,CAAC"}
1
+ {"version":3,"file":"account_route_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_route_schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAQvD,kFAAkF;AAClF,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAC3C,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,4CAA4C;AAC5C,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;kBAI9B,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEtE,4EAA4E;AAC5E,eAAO,MAAM,iCAAiC;;;iBAG5C,CAAC;AACH,MAAM,MAAM,iCAAiC,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAElG,oFAAoF;AACpF,eAAO,MAAM,UAAU;;;kBAGrB,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD,6EAA6E;AAC7E,eAAO,MAAM,WAAW;;kBAEtB,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD,2EAA2E;AAC3E,eAAO,MAAM,WAAW,WAAW,CAAC;AACpC,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD,wFAAwF;AACxF,eAAO,MAAM,YAAY;;;kBAGvB,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAExD,sHAAsH;AACtH,eAAO,MAAM,mBAAmB;;;kBAG9B,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEtE,uGAAuG;AACvG,eAAO,MAAM,oBAAoB;;;;kBAI/B,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,8CAA8C;AAC9C,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAUD,CAAC;AAEvC,8EAA8E;AAC9E,MAAM,WAAW,wBAAwB;IACxC,8FAA8F;IAC9F,0BAA0B,EAAE,OAAO,CAAC;CACpC;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,GACvC,SAAS,wBAAwB,KAC/B,CACF,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,EAC1B,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,EAC1B,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,EAC1B,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAoD1B,CAAC"}
@@ -56,7 +56,16 @@ export const PasswordChangeOutput = z.strictObject({
56
56
  sessions_revoked: z.number(),
57
57
  tokens_revoked: z.number()
58
58
  });
59
- /** Default maximum sessions per account. */
59
+ /**
60
+ * Default maximum sessions per account.
61
+ *
62
+ * The Rust twin pins the same 5 as a `const` (`fuz_auth`'s
63
+ * `DEFAULT_MAX_SESSIONS`) rather than a route-state field, because its
64
+ * `AccountRouteState` has no `Default` and a new field would break every
65
+ * exhaustive literal in every consumer workspace. So this stays the
66
+ * configurable side of the pair (`null` disables), and the two ship the same
67
+ * number — retuning means editing both.
68
+ */
60
69
  export const DEFAULT_MAX_SESSIONS = 5;
61
70
  /** Default maximum API tokens per account. */
62
71
  export const DEFAULT_MAX_TOKENS = 10;
@@ -69,20 +69,34 @@ export declare const DEFAULT_LOGIN_FAIL_FLOOR_MS = 250;
69
69
  */
70
70
  export declare const DEFAULT_LOGIN_FAIL_JITTER_MS = 25;
71
71
  /**
72
- * Shared options for route factories that create sessions and rate-limit by IP.
72
+ * Shared options for route factories that create sessions.
73
73
  *
74
74
  * Extended by `AccountRouteOptions` and `SignupRouteOptions`.
75
75
  * Consumers can destructure these from `AppServerContext` once and spread into multiple factories.
76
+ *
77
+ * The per-IP limiter is deliberately *not* here. Each auth surface names its
78
+ * own (`login_ip_rate_limiter`, `signup_ip_rate_limiter`,
79
+ * `bootstrap_ip_rate_limiter`) because the buckets are monotone within their
80
+ * window — see `RateLimiter.reset`. A shared base field made one instance the
81
+ * path of least resistance, which let a failure on any surface spend the
82
+ * budget that bounds guessing on every other one.
76
83
  */
77
84
  export interface AuthSessionRouteOptions {
78
85
  session_options: SessionOptions<string>;
79
- /** Rate limiter for auth attempts, keyed by client IP. Pass `null` to disable. */
80
- ip_rate_limiter: RateLimiter | null;
81
86
  }
82
87
  /**
83
88
  * Per-factory configuration for account route specs.
84
89
  */
85
90
  export interface AccountRouteOptions extends AuthSessionRouteOptions {
91
+ /**
92
+ * Rate limiter for login + password-change attempts, keyed by client IP.
93
+ * Pass `null` to disable. The distributed-spray backstop: login is the one
94
+ * surface that guesses *other* accounts' credentials, so this bucket is
95
+ * never refunded on success (see `RateLimiter.reset`). Password change
96
+ * shares it — it is password-bearing on the same account grain, and the
97
+ * Rust spine shares the same instance across both.
98
+ */
99
+ login_ip_rate_limiter: RateLimiter | null;
86
100
  /** Rate limiter for login attempts, keyed by submitted username. Pass `null` to disable. */
87
101
  login_account_rate_limiter: RateLimiter | null;
88
102
  /** Max active sessions per account. Evicts oldest on login. Default 5, `null` disables. */
@@ -130,7 +144,7 @@ export interface AccountRouteOptions extends AuthSessionRouteOptions {
130
144
  * Self-service session/token management is on `auth/account_actions.ts`.
131
145
  *
132
146
  * @param deps - stateless capabilities (keyring, password, log)
133
- * @param options - per-factory configuration (session_options, ip_rate_limiter, login_account_rate_limiter, bootstrap_status)
147
+ * @param options - per-factory configuration (session_options, login_ip_rate_limiter, login_account_rate_limiter, bootstrap_status)
134
148
  * @returns route specs (not yet applied to Hono)
135
149
  */
136
150
  export declare const create_account_route_specs: (deps: RouteFactoryDeps, options: AccountRouteOptions) => Array<RouteSpec>;
@@ -1 +1 @@
1
- {"version":3,"file":"account_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AA4B1D,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAExE,OAAO,EAAgC,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACpF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAGxE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gCAAgC,GAAI,UAAU,oBAAoB,KAAG,SA4EhF,CAAC;AAEH,iDAAiD;AACjD,MAAM,WAAW,oBAAoB;IACpC,yDAAyD;IACzD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8FAA8F;IAC9F,gBAAgB,CAAC,EAAE;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;CAC1C;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAQ/C;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACvC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,kFAAkF;IAClF,eAAe,EAAE,WAAW,GAAG,IAAI,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAoB,SAAQ,uBAAuB;IACnE,4FAA4F;IAC5F,0BAA0B,EAAE,WAAW,GAAG,IAAI,CAAC;IAC/C,2FAA2F;IAC3F,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC5C;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;CAC1C;AAID;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,0BAA0B,GACtC,MAAM,gBAAgB,EACtB,SAAS,mBAAmB,KAC1B,KAAK,CAAC,SAAS,CAwRjB,CAAC"}
1
+ {"version":3,"file":"account_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AA4B1D,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAExE,OAAO,EAAgC,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACpF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAGxE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gCAAgC,GAAI,UAAU,oBAAoB,KAAG,SA4EhF,CAAC;AAEH,iDAAiD;AACjD,MAAM,WAAW,oBAAoB;IACpC,yDAAyD;IACzD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8FAA8F;IAC9F,gBAAgB,CAAC,EAAE;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;CAC1C;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAQ/C;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,uBAAuB;IACvC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;CACxC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAoB,SAAQ,uBAAuB;IACnE;;;;;;;OAOG;IACH,qBAAqB,EAAE,WAAW,GAAG,IAAI,CAAC;IAC1C,4FAA4F;IAC5F,0BAA0B,EAAE,WAAW,GAAG,IAAI,CAAC;IAC/C,2FAA2F;IAC3F,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC5C;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;CAC1C;AAID;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,0BAA0B,GACtC,MAAM,gBAAgB,EACtB,SAAS,mBAAmB,KAC1B,KAAK,CAAC,SAAS,CA6RjB,CAAC"}
@@ -155,12 +155,12 @@ const login_fail_delay = (floor_ms, jitter_ms) => {
155
155
  * Self-service session/token management is on `auth/account_actions.ts`.
156
156
  *
157
157
  * @param deps - stateless capabilities (keyring, password, log)
158
- * @param options - per-factory configuration (session_options, ip_rate_limiter, login_account_rate_limiter, bootstrap_status)
158
+ * @param options - per-factory configuration (session_options, login_ip_rate_limiter, login_account_rate_limiter, bootstrap_status)
159
159
  * @returns route specs (not yet applied to Hono)
160
160
  */
161
161
  export const create_account_route_specs = (deps, options) => {
162
162
  const { keyring, password } = deps;
163
- const { session_options, ip_rate_limiter, login_account_rate_limiter, max_sessions = DEFAULT_MAX_SESSIONS, login_fail_floor_ms = DEFAULT_LOGIN_FAIL_FLOOR_MS, login_fail_jitter_ms = DEFAULT_LOGIN_FAIL_JITTER_MS, connection_closer = null, bootstrap_status } = options;
163
+ const { session_options, login_ip_rate_limiter, login_account_rate_limiter, max_sessions = DEFAULT_MAX_SESSIONS, login_fail_floor_ms = DEFAULT_LOGIN_FAIL_FLOOR_MS, login_fail_jitter_ms = DEFAULT_LOGIN_FAIL_JITTER_MS, connection_closer = null, bootstrap_status } = options;
164
164
  const [verify_shape, login_shape, logout_shape, password_shape] = create_account_route_shapes({
165
165
  login_account_rate_limited: login_account_rate_limiter !== null
166
166
  });
@@ -181,9 +181,9 @@ export const create_account_route_specs = (deps, options) => {
181
181
  ...login_shape,
182
182
  handler: async (c, route) => {
183
183
  // Per-IP rate limit check (before any DB/password work)
184
- const ip = ip_rate_limiter ? get_client_ip(c) : null;
185
- if (ip_rate_limiter && ip) {
186
- const check = ip_rate_limiter.check(ip);
184
+ const ip = login_ip_rate_limiter ? get_client_ip(c) : null;
185
+ if (login_ip_rate_limiter && ip) {
186
+ const check = login_ip_rate_limiter.check(ip);
187
187
  if (!check.allowed) {
188
188
  return rate_limit_exceeded_response(c, check.retry_after);
189
189
  }
@@ -214,8 +214,8 @@ export const create_account_route_specs = (deps, options) => {
214
214
  // enumeration prevention: verify_dummy equalizes Argon2id timing;
215
215
  // login_fail_delay equalizes every other path difference.
216
216
  await password.verify_dummy(pw);
217
- if (ip_rate_limiter && ip)
218
- ip_rate_limiter.record(ip);
217
+ if (login_ip_rate_limiter && ip)
218
+ login_ip_rate_limiter.record(ip);
219
219
  if (login_account_rate_limiter)
220
220
  login_account_rate_limiter.record(account_rate_key);
221
221
  deps.audit.emit(route, {
@@ -229,8 +229,8 @@ export const create_account_route_specs = (deps, options) => {
229
229
  }
230
230
  const valid = await password.verify_password(pw, account.password_hash);
231
231
  if (!valid) {
232
- if (ip_rate_limiter && ip)
233
- ip_rate_limiter.record(ip);
232
+ if (login_ip_rate_limiter && ip)
233
+ login_ip_rate_limiter.record(ip);
234
234
  if (login_account_rate_limiter)
235
235
  login_account_rate_limiter.record(account_rate_key);
236
236
  deps.audit.emit(route, {
@@ -243,9 +243,11 @@ export const create_account_route_specs = (deps, options) => {
243
243
  await delay;
244
244
  return c.json({ error: ERROR_INVALID_CREDENTIALS }, 401);
245
245
  }
246
- // Successful login — reset rate limits
247
- if (ip_rate_limiter && ip)
248
- ip_rate_limiter.reset(ip);
246
+ // Successful login — clear the account-grain failure history only.
247
+ // The IP aggregate is deliberately left standing: it is the
248
+ // distributed-spray backstop, so a success on one account must not
249
+ // refund the budget shared by every other account reachable from
250
+ // that address. See `RateLimiter.reset`.
249
251
  if (login_account_rate_limiter)
250
252
  login_account_rate_limiter.reset(account_rate_key);
251
253
  await create_session_and_set_cookie({
@@ -305,9 +307,9 @@ export const create_account_route_specs = (deps, options) => {
305
307
  ...password_shape,
306
308
  handler: async (c, route) => {
307
309
  // per-IP rate limit check (before argon2 work)
308
- const ip = ip_rate_limiter ? get_client_ip(c) : null;
309
- if (ip_rate_limiter && ip) {
310
- const check = ip_rate_limiter.check(ip);
310
+ const ip = login_ip_rate_limiter ? get_client_ip(c) : null;
311
+ if (login_ip_rate_limiter && ip) {
312
+ const check = login_ip_rate_limiter.check(ip);
311
313
  if (!check.allowed) {
312
314
  return rate_limit_exceeded_response(c, check.retry_after);
313
315
  }
@@ -325,8 +327,8 @@ export const create_account_route_specs = (deps, options) => {
325
327
  }
326
328
  const valid = await password.verify_password(current_password, ctx.account.password_hash);
327
329
  if (!valid) {
328
- if (ip_rate_limiter && ip)
329
- ip_rate_limiter.record(ip);
330
+ if (login_ip_rate_limiter && ip)
331
+ login_ip_rate_limiter.record(ip);
330
332
  if (login_account_rate_limiter)
331
333
  login_account_rate_limiter.record(ctx.account.id);
332
334
  deps.audit.emit(route, {
@@ -350,16 +352,17 @@ export const create_account_route_specs = (deps, options) => {
350
352
  // incidental; password is account-level state).
351
353
  const updated = await query_update_account_password(route, ctx.account.id, new_hash, null, ctx.account.password_hash);
352
354
  // Verify-success contract — the caller proved knowledge, so wipe
353
- // their failure history. The race-loser branch below re-records
354
- // one on top of the wiped slate so net cost stays 1 (mirrors the
355
- // verify-fail branch above's `record`-from-prior+1 outcome when
356
- // prior was 0; for prior > 0 the race-loser pays exactly 1,
357
- // matching the OLD pre-S1 behavior). Deferring from "after
358
- // verify" to "after UPDATE settled" is what closes the S1
359
- // bypass — a throw between reset and the UPDATE could have
360
- // wiped an attacker's budget.
361
- if (ip_rate_limiter && ip)
362
- ip_rate_limiter.reset(ip);
355
+ // their account-grain failure history. The race-loser branch below
356
+ // re-records one on top of the wiped slate so net cost stays 1 on
357
+ // that bucket. Deferring from "after verify" to "after UPDATE
358
+ // settled" is what closes the S1 bypass — a throw between reset
359
+ // and the UPDATE could have wiped an attacker's budget.
360
+ //
361
+ // The IP aggregate is deliberately not cleared: it is the same
362
+ // instance login uses, so refunding it here reopens the spray path
363
+ // through a route that only needs the caller's *own* password. Its
364
+ // record on the race-loser branch below therefore accumulates
365
+ // rather than netting out, which is the intent.
363
366
  if (login_account_rate_limiter)
364
367
  login_account_rate_limiter.reset(ctx.account.id);
365
368
  if (!updated) {
@@ -370,8 +373,8 @@ export const create_account_route_specs = (deps, options) => {
370
373
  // audit log can distinguish "user typoed" from "two clients
371
374
  // raced." Sessions/tokens were already revoked by the winner;
372
375
  // no cookie clear here either.
373
- if (ip_rate_limiter && ip)
374
- ip_rate_limiter.record(ip);
376
+ if (login_ip_rate_limiter && ip)
377
+ login_ip_rate_limiter.record(ip);
375
378
  if (login_account_rate_limiter)
376
379
  login_account_rate_limiter.record(ctx.account.id);
377
380
  deps.audit.emit(route, {
@@ -82,6 +82,10 @@ export declare const query_api_token_list_for_account: (deps: QueryDeps, account
82
82
  * `(account_id, credential_kind)` before count/evict/insert — see the matching
83
83
  * note on `query_session_enforce_limit`.
84
84
  *
85
+ * Ordering carries the same `id DESC` stability tie-breaker, with the same
86
+ * caveat, as `query_session_enforce_limit` — deterministic survivors under a
87
+ * `created_at` tie, not a guarantee that the row just inserted survives.
88
+ *
85
89
  * @param deps - query dependencies (must be transaction-scoped)
86
90
  * @param account_id - the account to enforce the limit for
87
91
  * @param max_tokens - maximum number of tokens to keep
@@ -1 +1 @@
1
- {"version":3,"file":"api_token_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/api_token_queries.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,KAAK,UAAU,EAAmD,MAAM,kBAAkB,CAAC;AAEpG,yEAAyE;AACzE,MAAM,WAAW,iBAAkB,SAAQ,SAAS;IACnD,GAAG,EAAE,MAAM,CAAC;CACZ;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,SAAS,EACf,IAAI,MAAM,EACV,YAAY,MAAM,EAClB,MAAM,MAAM,EACZ,YAAY,MAAM,EAClB,OAAO,UAAU,EACjB,aAAa,IAAI,GAAG,IAAI,KACtB,OAAO,CAAC,QAAQ,CAelB,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,wBAAwB,GACpC,MAAM,iBAAiB,EACvB,WAAW,MAAM,EACjB,IAAI,MAAM,GAAG,SAAS,EACtB,iBAAiB,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,KAC/C,OAAO,CAAC,QAAQ,GAAG,SAAS,CAgC9B,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,uCAAuC,GACnD,MAAM,SAAS,EACf,YAAY,MAAM,KAChB,OAAO,CAAC,MAAM,CAMhB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kCAAkC,GAC9C,MAAM,SAAS,EACf,IAAI,MAAM,EACV,YAAY,MAAM,KAChB,OAAO,CAAC,OAAO,CAMjB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,GAC5C,MAAM,SAAS,EACf,YAAY,MAAM,KAChB,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAM7C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,6BAA6B,GACzC,MAAM,SAAS,EACf,YAAY,MAAM,EAClB,YAAY,MAAM,KAChB,OAAO,CAAC,MAAM,CAYhB,CAAC"}
1
+ {"version":3,"file":"api_token_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/api_token_queries.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,KAAK,UAAU,EAAmD,MAAM,kBAAkB,CAAC;AAEpG,yEAAyE;AACzE,MAAM,WAAW,iBAAkB,SAAQ,SAAS;IACnD,GAAG,EAAE,MAAM,CAAC;CACZ;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,SAAS,EACf,IAAI,MAAM,EACV,YAAY,MAAM,EAClB,MAAM,MAAM,EACZ,YAAY,MAAM,EAClB,OAAO,UAAU,EACjB,aAAa,IAAI,GAAG,IAAI,KACtB,OAAO,CAAC,QAAQ,CAelB,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,wBAAwB,GACpC,MAAM,iBAAiB,EACvB,WAAW,MAAM,EACjB,IAAI,MAAM,GAAG,SAAS,EACtB,iBAAiB,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,KAC/C,OAAO,CAAC,QAAQ,GAAG,SAAS,CAgC9B,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,uCAAuC,GACnD,MAAM,SAAS,EACf,YAAY,MAAM,KAChB,OAAO,CAAC,MAAM,CAMhB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kCAAkC,GAC9C,MAAM,SAAS,EACf,IAAI,MAAM,EACV,YAAY,MAAM,KAChB,OAAO,CAAC,OAAO,CAMjB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,GAC5C,MAAM,SAAS,EACf,YAAY,MAAM,KAChB,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAM7C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,6BAA6B,GACzC,MAAM,SAAS,EACf,YAAY,MAAM,EAClB,YAAY,MAAM,KAChB,OAAO,CAAC,MAAM,CAYhB,CAAC"}
@@ -125,6 +125,10 @@ export const query_api_token_list_for_account = async (deps, account_id) => {
125
125
  * `(account_id, credential_kind)` before count/evict/insert — see the matching
126
126
  * note on `query_session_enforce_limit`.
127
127
  *
128
+ * Ordering carries the same `id DESC` stability tie-breaker, with the same
129
+ * caveat, as `query_session_enforce_limit` — deterministic survivors under a
130
+ * `created_at` tie, not a guarantee that the row just inserted survives.
131
+ *
128
132
  * @param deps - query dependencies (must be transaction-scoped)
129
133
  * @param account_id - the account to enforce the limit for
130
134
  * @param max_tokens - maximum number of tokens to keep
@@ -136,7 +140,7 @@ export const query_api_token_enforce_limit = async (deps, account_id, max_tokens
136
140
  WHERE id IN (
137
141
  SELECT id FROM api_token
138
142
  WHERE account_id = $1
139
- ORDER BY created_at DESC
143
+ ORDER BY created_at DESC, id DESC
140
144
  OFFSET $2
141
145
  ) RETURNING id`, [account_id, max_tokens]);
142
146
  return rows.length;
@@ -49,12 +49,16 @@ import { type AuditLogConfig, type AuditLogEvent, type AuditLogInput } from './a
49
49
  * `pending_effects` queue. The bound emitter carries its own `log`
50
50
  * reference inside the closure, so per-call contexts don't need one.
51
51
  *
52
- * Audit emits are eager-only by design: the bound emitter fires the
52
+ * Audit emits are eager by default: the bound emitter fires the
53
53
  * pool write immediately and pushes the in-flight `Promise<void>` here.
54
- * They never go through `emit_after_commit` — pool-routed audit writes
55
- * are already rollback-resilient because they run outside the request
56
- * transaction, so the post-commit timing the deferred queue provides
57
- * would only delay forensic visibility without any safety benefit.
54
+ * Attempt/failure audits never go through `emit_after_commit` —
55
+ * pool-routed writes are already rollback-resilient because they run
56
+ * outside the request transaction, so deferring them would only delay
57
+ * forensic visibility without any safety benefit. The exception is a
58
+ * **success-only** event paired with a state mutation (the
59
+ * `db_admin_row_delete` emit in `http/db_routes.ts`): there the caller
60
+ * wraps the emit in `emit_after_commit` so the trail can't claim a
61
+ * mutation whose transaction failed at COMMIT.
58
62
  *
59
63
  * Both `RouteContext` and `ActionContext` structurally satisfy this
60
64
  * shape (they each carry `pending_effects`), so handlers pass `route`
@@ -1 +1 @@
1
- {"version":3,"file":"audit_emitter.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/audit_emitter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAEnD,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,OAAO,EAEN,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,MAAM,uBAAuB,CAAC;AAE/B;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,mBAAmB;IACnC,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;CACtC;AAED;;;;GAIG;AACH,MAAM,WAAW,yBAA0B,SAAQ,mBAAmB;IACrE,0FAA0F;IAC1F,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC5B;;;;;;;;;;;;;;;;OAgBG;IACH,IAAI,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,mBAAmB,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAChF;;;;;;;OAOG;IACH,sBAAsB,CAAC,CAAC,SAAS,MAAM,EACtC,GAAG,EAAE,yBAAyB,EAC9B,IAAI,EAAE,mBAAmB,EACzB,KAAK,EAAE;QACN,UAAU,EAAE,CAAC,CAAC;QACd,iBAAiB,EAAE,IAAI,GAAG,IAAI,CAAC;QAC/B,eAAe,EAAE,IAAI,GAAG,IAAI,CAAC;QAC7B,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;QACvC,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;KAChC,GACC,IAAI,CAAC;IACR;;;;;;;;;;OAUG;IACH,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE;;;;;;;OAOG;IACH,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IACnC;;;;;;;;;;;;;OAaG;IACH,YAAY,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAAG,IAAI,CAAC;IAC7D,+EAA+E;IAC/E,cAAc,IAAI,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,SAAS,MAAM,EAC1C,GAAG,EAAE,mBAAmB,EACxB,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,KACnB,IAAI,CAAC;AAEV;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,WAAW,KAAK,WAAW,CAAC;AAEhE,0CAA0C;AAC1C,MAAM,WAAW,yBAAyB;IACzC,oDAAoD;IACpD,EAAE,EAAE,EAAE,CAAC;IACP,qDAAqD;IACrD,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,cAAc,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACzD;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,cAAc,CAAC;IAClC;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,yBAAyB,KAAG,YAuFzE,CAAC"}
1
+ {"version":3,"file":"audit_emitter.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/audit_emitter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAEnD,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,OAAO,EAEN,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,MAAM,uBAAuB,CAAC;AAE/B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,mBAAmB;IACnC,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;CACtC;AAED;;;;GAIG;AACH,MAAM,WAAW,yBAA0B,SAAQ,mBAAmB;IACrE,0FAA0F;IAC1F,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC5B;;;;;;;;;;;;;;;;OAgBG;IACH,IAAI,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,mBAAmB,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAChF;;;;;;;OAOG;IACH,sBAAsB,CAAC,CAAC,SAAS,MAAM,EACtC,GAAG,EAAE,yBAAyB,EAC9B,IAAI,EAAE,mBAAmB,EACzB,KAAK,EAAE;QACN,UAAU,EAAE,CAAC,CAAC;QACd,iBAAiB,EAAE,IAAI,GAAG,IAAI,CAAC;QAC/B,eAAe,EAAE,IAAI,GAAG,IAAI,CAAC;QAC7B,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;QACvC,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;KAChC,GACC,IAAI,CAAC;IACR;;;;;;;;;;OAUG;IACH,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE;;;;;;;OAOG;IACH,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IACnC;;;;;;;;;;;;;OAaG;IACH,YAAY,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAAG,IAAI,CAAC;IAC7D,+EAA+E;IAC/E,cAAc,IAAI,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,SAAS,MAAM,EAC1C,GAAG,EAAE,mBAAmB,EACxB,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,KACnB,IAAI,CAAC;AAEV;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,WAAW,KAAK,WAAW,CAAC;AAEhE,0CAA0C;AAC1C,MAAM,WAAW,yBAAyB;IACzC,oDAAoD;IACpD,EAAE,EAAE,EAAE,CAAC;IACP,qDAAqD;IACrD,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,cAAc,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACzD;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,cAAc,CAAC;IAClC;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,yBAAyB,KAAG,YAuFzE,CAAC"}
@@ -16,7 +16,7 @@ import { Uuid } from '@fuzdev/fuz_util/id.ts';
16
16
  * Not a security boundary — in-process code has many other paths to subvert
17
17
  * audit logging.
18
18
  */
19
- export declare const AUDIT_EVENT_TYPES: readonly ["login", "logout", "bootstrap", "signup", "password_change", "session_revoke", "session_revoke_all", "token_create", "token_revoke", "token_revoke_all", "role_grant_create", "role_grant_revoke", "role_grant_offer_create", "role_grant_offer_accept", "role_grant_offer_decline", "role_grant_offer_retract", "role_grant_offer_expire", "role_grant_offer_supersede", "invite_create", "invite_delete", "account_delete", "account_purge", "account_undelete", "actor_delete", "actor_purge", "actor_undelete", "app_settings_update"];
19
+ export declare const AUDIT_EVENT_TYPES: readonly ["login", "logout", "bootstrap", "signup", "password_change", "session_revoke", "session_revoke_all", "token_create", "token_revoke", "token_revoke_all", "role_grant_create", "role_grant_revoke", "role_grant_offer_create", "role_grant_offer_accept", "role_grant_offer_decline", "role_grant_offer_retract", "role_grant_offer_expire", "role_grant_offer_supersede", "invite_create", "invite_delete", "account_delete", "account_purge", "account_undelete", "actor_delete", "actor_purge", "actor_undelete", "app_settings_update", "db_admin_row_delete"];
20
20
  /** Zod schema for audit event types. */
21
21
  export declare const AuditEventType: z.ZodEnum<{
22
22
  bootstrap: "bootstrap";
@@ -46,6 +46,7 @@ export declare const AuditEventType: z.ZodEnum<{
46
46
  actor_purge: "actor_purge";
47
47
  actor_undelete: "actor_undelete";
48
48
  app_settings_update: "app_settings_update";
49
+ db_admin_row_delete: "db_admin_row_delete";
49
50
  }>;
50
51
  export type AuditEventType = z.infer<typeof AuditEventType>;
51
52
  /**
@@ -230,6 +231,11 @@ export declare const audit_metadata_schemas: Readonly<{
230
231
  old_value: z.ZodUnknown;
231
232
  new_value: z.ZodUnknown;
232
233
  }, z.core.$loose>;
234
+ db_admin_row_delete: z.ZodObject<{
235
+ table: z.ZodString;
236
+ pk_column: z.ZodString;
237
+ id: z.ZodString;
238
+ }, z.core.$loose>;
233
239
  }>;
234
240
  /** Mapped type of metadata shapes per event type, derived from Zod schemas. */
235
241
  export type AuditMetadataMap = {
@@ -1 +1 @@
1
- {"version":3,"file":"audit_log_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/audit_log_schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"audit_log_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/audit_log_schema.ts"],"names":[],"mappings":"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"}
@@ -57,7 +57,8 @@ export const AUDIT_EVENT_TYPES = Object.freeze([
57
57
  'actor_delete',
58
58
  'actor_purge',
59
59
  'actor_undelete',
60
- 'app_settings_update'
60
+ 'app_settings_update',
61
+ 'db_admin_row_delete'
61
62
  ]);
62
63
  /** Zod schema for audit event types. */
63
64
  export const AuditEventType = z.enum(AUDIT_EVENT_TYPES);
@@ -344,6 +345,20 @@ export const audit_metadata_schemas = Object.freeze({
344
345
  setting: z.string().meta({ description: 'Name of the setting that changed.' }),
345
346
  old_value: z.unknown().meta({ description: 'Setting value before the update.' }),
346
347
  new_value: z.unknown().meta({ description: 'Setting value after the update.' })
348
+ }),
349
+ // Row deleted through the generic db-admin table browser
350
+ // (`http/db_routes.ts` / the Rust spine's `fuz_db_admin`). Account-grain
351
+ // attribution (`account_id`, no `actor_id`) — the browser's gate is
352
+ // account-grain by construction. Emitted only on a successful delete;
353
+ // post-composite-refusal the delete is provably single-row, so no count.
354
+ db_admin_row_delete: z.looseObject({
355
+ table: z.string().meta({ description: 'Table the row was deleted from.' }),
356
+ pk_column: z.string().meta({
357
+ description: 'Single primary-key column the `DELETE` filtered on.'
358
+ }),
359
+ id: z.string().meta({
360
+ description: "Primary-key value of the deleted row, as the row's canonical `::text` rendering."
361
+ })
347
362
  })
348
363
  });
349
364
  /**
@@ -17,7 +17,6 @@
17
17
  import type { MiddlewareHandler } from 'hono';
18
18
  import type { Logger } from '@fuzdev/fuz_util/log.ts';
19
19
  import type { QueryDeps } from '../db/query_deps.js';
20
- import { type RateLimiter } from '../rate_limiter.js';
21
20
  /**
22
21
  * Create middleware that authenticates via bearer token.
23
22
  *
@@ -37,14 +36,18 @@ import { type RateLimiter } from '../rate_limiter.js';
37
36
  * (e.g. by session middleware). Acting-actor resolution + `RequestContext`
38
37
  * construction are deferred to the dispatcher's authorization phase.
39
38
  *
40
- * Rate limiting (429) is the only hard-fail — it's a throttling concern
41
- * independent of auth identity.
39
+ * There is deliberately **no rate limit on this path**, and no 429 — every
40
+ * failure soft-fails to "no credential". An API token is 32 bytes of CSPRNG
41
+ * output resolved by a blake3 hash lookup, so guessing is bounded by entropy,
42
+ * not by throttling; a limiter here would buy nothing measurable while costing
43
+ * availability (the check/record has to precede the async lookup to close its
44
+ * own TOCTOU window, so concurrent requests bearing a *valid* token race each
45
+ * other into a 429). The Rust spine never had one here; this is the converged
46
+ * shape. See `docs/security.md` §Why bearer auth is not rate limited.
42
47
  *
43
48
  * @param deps - query dependencies (pool-level db for middleware)
44
- * @param ip_rate_limiter - per-IP rate limiter for bearer token attempts (null to disable)
45
49
  * @param log - the logger instance
46
50
  * @mutates Hono context - sets `ACCOUNT_ID_KEY`, `CREDENTIAL_TYPE_KEY`, and `AUTH_API_TOKEN_ID_KEY` on success
47
- * @mutates `ip_rate_limiter` - records on attempt; resets on a valid token
48
51
  */
49
- export declare const create_bearer_auth_middleware: (deps: QueryDeps, ip_rate_limiter: RateLimiter | null, log: Logger) => MiddlewareHandler;
52
+ export declare const create_bearer_auth_middleware: (deps: QueryDeps, log: Logger) => MiddlewareHandler;
50
53
  //# sourceMappingURL=bearer_auth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bearer_auth.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/bearer_auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAC9C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAUtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGrD,OAAO,EAAgC,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEpF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,6BAA6B,GACzC,MAAM,SAAS,EACf,iBAAiB,WAAW,GAAG,IAAI,EACnC,KAAK,MAAM,KACT,iBAoFF,CAAC"}
1
+ {"version":3,"file":"bearer_auth.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/bearer_auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAC9C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAUtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAIrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,6BAA6B,GAAI,MAAM,SAAS,EAAE,KAAK,MAAM,KAAG,iBAuE5E,CAAC"}
@@ -20,7 +20,6 @@ import { parse_stored_token_scope, token_scope_full } from './token_scope.js';
20
20
  import { query_validate_api_token } from './api_token_queries.js';
21
21
  import { get_client_ip } from '../http/client_ip.js';
22
22
  import { is_browser_context } from '../http/origin.js';
23
- import { rate_limit_exceeded_response } from '../rate_limiter.js';
24
23
  /**
25
24
  * Create middleware that authenticates via bearer token.
26
25
  *
@@ -40,16 +39,20 @@ import { rate_limit_exceeded_response } from '../rate_limiter.js';
40
39
  * (e.g. by session middleware). Acting-actor resolution + `RequestContext`
41
40
  * construction are deferred to the dispatcher's authorization phase.
42
41
  *
43
- * Rate limiting (429) is the only hard-fail — it's a throttling concern
44
- * independent of auth identity.
42
+ * There is deliberately **no rate limit on this path**, and no 429 — every
43
+ * failure soft-fails to "no credential". An API token is 32 bytes of CSPRNG
44
+ * output resolved by a blake3 hash lookup, so guessing is bounded by entropy,
45
+ * not by throttling; a limiter here would buy nothing measurable while costing
46
+ * availability (the check/record has to precede the async lookup to close its
47
+ * own TOCTOU window, so concurrent requests bearing a *valid* token race each
48
+ * other into a 429). The Rust spine never had one here; this is the converged
49
+ * shape. See `docs/security.md` §Why bearer auth is not rate limited.
45
50
  *
46
51
  * @param deps - query dependencies (pool-level db for middleware)
47
- * @param ip_rate_limiter - per-IP rate limiter for bearer token attempts (null to disable)
48
52
  * @param log - the logger instance
49
53
  * @mutates Hono context - sets `ACCOUNT_ID_KEY`, `CREDENTIAL_TYPE_KEY`, and `AUTH_API_TOKEN_ID_KEY` on success
50
- * @mutates `ip_rate_limiter` - records on attempt; resets on a valid token
51
54
  */
52
- export const create_bearer_auth_middleware = (deps, ip_rate_limiter, log) => {
55
+ export const create_bearer_auth_middleware = (deps, log) => {
53
56
  return async (c, next) => {
54
57
  // Skip if an account is already authenticated (e.g. by session middleware)
55
58
  if (c.get(ACCOUNT_ID_KEY) != null) {
@@ -88,28 +91,16 @@ export const create_bearer_auth_middleware = (deps, ip_rate_limiter, log) => {
88
91
  await next();
89
92
  return;
90
93
  }
94
+ // `ip` feeds the token's `last_used_ip` bookkeeping, not a limiter.
91
95
  const ip = get_client_ip(c);
92
- // Per-IP rate limit: record before async DB work to close the TOCTOU
93
- // window where concurrent requests could all pass check() before any
94
- // reaches record(). On valid token, reset the counter below.
95
- if (ip_rate_limiter) {
96
- const check = ip_rate_limiter.check(ip);
97
- if (!check.allowed) {
98
- return rate_limit_exceeded_response(c, check.retry_after);
99
- }
100
- ip_rate_limiter.record(ip);
101
- }
102
96
  const api_token = await query_validate_api_token({ ...deps, log }, raw_token, ip, c.var.pending_effects);
103
97
  if (!api_token) {
104
- // Invalid or expired token — soft-fail. Rate limit counter stays
105
- // incremented (recorded above), correctly penalizing bad attempts.
98
+ // Invalid or expired token — soft-fail, indistinguishable from
99
+ // sending no credential at all.
106
100
  log.debug('bearer auth soft-fail: token not found or expired');
107
101
  await next();
108
102
  return;
109
103
  }
110
- // Valid token — reset rate limit counter
111
- if (ip_rate_limiter)
112
- ip_rate_limiter.reset(ip);
113
104
  c.set(ACCOUNT_ID_KEY, api_token.account_id);
114
105
  c.set(CREDENTIAL_TYPE_KEY, 'api_token');
115
106
  c.set(AUTH_API_TOKEN_ID_KEY, api_token.id);
@@ -37,8 +37,18 @@ export interface BootstrapRouteOptions {
37
37
  * Use for app-specific post-bootstrap work like generating API tokens.
38
38
  */
39
39
  on_bootstrap?: (result: BootstrapAccountSuccess, c: Context) => Promise<void>;
40
- /** Rate limiter for bootstrap attempts (per-IP). Pass `null` to disable. */
41
- ip_rate_limiter: RateLimiter | null;
40
+ /**
41
+ * Rate limiter for bootstrap attempts, keyed by client IP. Pass `null` to
42
+ * disable. Its own instance, not login's: bootstrap is one-shot and its
43
+ * bucket is never refunded on success (see `RateLimiter.reset`), so a
44
+ * fumbled token would otherwise leave the operator's *login* budget nearly
45
+ * spent on a deployment where their new account is the only one that
46
+ * exists. The Rust spine rate-limits bootstrap not at all (the token is
47
+ * 32 bytes of CSPRNG compared in constant time); this is the tighter side
48
+ * of that divergence, kept because the token also sits in a file whose
49
+ * read path an operator can misconfigure.
50
+ */
51
+ bootstrap_ip_rate_limiter: RateLimiter | null;
42
52
  }
43
53
  /**
44
54
  * Dependencies for checking bootstrap status at startup.
@@ -1 +1 @@
1
- {"version":3,"file":"bootstrap_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/bootstrap_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEpC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,EAAqB,KAAK,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEzF,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAExE,OAAO,EAAgC,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACpF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGrD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACrC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,8EAA8E;IAC9E,gBAAgB,EAAE,eAAe,CAAC;IAClC;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,uBAAuB,EAAE,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E,4EAA4E;IAC5E,eAAe,EAAE,WAAW,GAAG,IAAI,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACxC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACnD,EAAE,EAAE,EAAE,CAAC;IACP,GAAG,EAAE,MAAM,CAAC;CACZ;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,wBAAwB,EAC9B,SAAS;IAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,KACpC,OAAO,CAAC,eAAe,CAwBzB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,GACxC,MAAM,gBAAgB,EACtB,SAAS,qBAAqB,KAC5B,KAAK,CAAC,SAAS,CAmGjB,CAAC"}
1
+ {"version":3,"file":"bootstrap_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/bootstrap_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEpC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,EAAqB,KAAK,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEzF,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAExE,OAAO,EAAgC,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACpF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGrD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACrC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,8EAA8E;IAC9E,gBAAgB,EAAE,eAAe,CAAC;IAClC;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,uBAAuB,EAAE,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E;;;;;;;;;;OAUG;IACH,yBAAyB,EAAE,WAAW,GAAG,IAAI,CAAC;CAC9C;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACxC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACnD,EAAE,EAAE,EAAE,CAAC;IACP,GAAG,EAAE,MAAM,CAAC;CACZ;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,wBAAwB,EAC9B,SAAS;IAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,KACpC,OAAO,CAAC,eAAe,CAwBzB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,GACxC,MAAM,gBAAgB,EACtB,SAAS,qBAAqB,KAC5B,KAAK,CAAC,SAAS,CAmHjB,CAAC"}