@feltdb/core 0.8.7 → 0.9.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 (75) hide show
  1. package/dist/authority-scope.d.ts +14 -0
  2. package/dist/authority-scope.d.ts.map +1 -0
  3. package/dist/authority-scope.js +34 -0
  4. package/dist/collection.d.ts.map +1 -1
  5. package/dist/collection.js +26 -1
  6. package/dist/create/package-versions.js +1 -1
  7. package/dist/create/server-source/crates/feltdb/src/lib.rs +343 -1
  8. package/dist/create/server-source/crates/feltdb/src/state_contract.rs +465 -107
  9. package/dist/create/server-source/crates/feltdb/src/state_model.rs +15 -2
  10. package/dist/create/server-source/crates/feltdb-server/src/control_plane.rs +845 -0
  11. package/dist/create/server-source/crates/feltdb-server/src/lib.rs +1 -0
  12. package/dist/create/server-source/crates/feltdb-server/src/main.rs +1349 -5
  13. package/dist/create/server-source/crates/feltdb-server/src/principals.rs +37 -0
  14. package/dist/create/server-source/crates/feltdb-wasm/src/lib.rs +1 -1
  15. package/dist/db.d.ts +10 -1
  16. package/dist/db.d.ts.map +1 -1
  17. package/dist/db.js +46 -4
  18. package/dist/error-codes.d.ts +7 -1
  19. package/dist/error-codes.d.ts.map +1 -1
  20. package/dist/error-codes.js +20 -4
  21. package/dist/file-db.d.ts.map +1 -1
  22. package/dist/file-db.js +5 -1
  23. package/dist/http-db.d.ts +11 -0
  24. package/dist/http-db.d.ts.map +1 -1
  25. package/dist/http-db.js +11 -3
  26. package/dist/index-core.d.ts +1 -0
  27. package/dist/index-core.d.ts.map +1 -1
  28. package/dist/index-core.js +1 -0
  29. package/dist/operation-admission.d.ts +5 -0
  30. package/dist/operation-admission.d.ts.map +1 -1
  31. package/dist/operation-admission.js +0 -7
  32. package/dist/studio/app.d.ts.map +1 -1
  33. package/dist/studio/components/index.js +2 -2
  34. package/dist/studio/{components-Duq2xQWt.js → components-CehkcX_k.js} +2355 -2355
  35. package/dist/studio/control-plane/Agents.d.ts +6 -0
  36. package/dist/studio/control-plane/Agents.d.ts.map +1 -0
  37. package/dist/studio/control-plane/Data.d.ts +10 -0
  38. package/dist/studio/control-plane/Data.d.ts.map +1 -0
  39. package/dist/studio/control-plane/Instance.d.ts +7 -0
  40. package/dist/studio/control-plane/Instance.d.ts.map +1 -0
  41. package/dist/studio/control-plane/Operations.d.ts +6 -0
  42. package/dist/studio/control-plane/Operations.d.ts.map +1 -0
  43. package/dist/studio/control-plane/Overview.d.ts +4 -0
  44. package/dist/studio/control-plane/Overview.d.ts.map +1 -0
  45. package/dist/studio/control-plane/Schema.d.ts +4 -0
  46. package/dist/studio/control-plane/Schema.d.ts.map +1 -0
  47. package/dist/studio/control-plane/State.d.ts +4 -0
  48. package/dist/studio/control-plane/State.d.ts.map +1 -0
  49. package/dist/studio/control-plane/context.d.ts +42 -0
  50. package/dist/studio/control-plane/context.d.ts.map +1 -0
  51. package/dist/studio/control-plane/index.d.ts +18 -0
  52. package/dist/studio/control-plane/index.d.ts.map +1 -0
  53. package/dist/studio/control-plane/index.js +2 -0
  54. package/dist/studio/control-plane/primitives.d.ts +70 -0
  55. package/dist/studio/control-plane/primitives.d.ts.map +1 -0
  56. package/dist/studio/control-plane-D6TT2fCQ.js +3064 -0
  57. package/dist/studio/index.d.ts +2 -0
  58. package/dist/studio/index.d.ts.map +1 -1
  59. package/dist/studio/index.js +326 -323
  60. package/dist/studio/studio.css +1 -1
  61. package/dist/studio/utils/control-plane-api.d.ts +442 -0
  62. package/dist/studio/utils/control-plane-api.d.ts.map +1 -0
  63. package/dist/studio/utils/control-plane-api.js +204 -0
  64. package/dist/studio/utils/index.d.ts +1 -0
  65. package/dist/studio/utils/index.d.ts.map +1 -1
  66. package/dist/studio/utils/index.js +13 -12
  67. package/dist/studio-app/assets/{feltdb_wasm-C1VhI-U5.js → feltdb_wasm-DksXDnLa.js} +1 -1
  68. package/dist/studio-app/assets/feltdb_wasm_bg-Yy4QRHJT.wasm +0 -0
  69. package/dist/studio-app/assets/{index-Cd_NPw14.css → index-BrsF-kWL.css} +1 -1
  70. package/dist/studio-app/assets/index-CjELc-ul.js +30 -0
  71. package/dist/studio-app/index.html +2 -2
  72. package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
  73. package/package.json +1 -1
  74. package/dist/studio-app/assets/feltdb_wasm_bg-C8HXbAXb.wasm +0 -0
  75. package/dist/studio-app/assets/index-Bbos1m2U.js +0 -29
@@ -0,0 +1,14 @@
1
+ export interface TenantAuthorityScope {
2
+ kind: 'tenant';
3
+ tenantId: string;
4
+ environmentId?: string;
5
+ }
6
+ export interface PlatformAuthorityScope {
7
+ kind: 'platform';
8
+ }
9
+ export type AuthorityScope = TenantAuthorityScope | PlatformAuthorityScope;
10
+ export declare function normalizeAuthorityScope(scope: AuthorityScope | undefined): AuthorityScope;
11
+ export declare function authorityScopeKey(scope: AuthorityScope): string;
12
+ export declare function sameAuthorityScope(left: AuthorityScope, right: AuthorityScope): boolean;
13
+ export declare function assertAuthorityScope(expected: AuthorityScope, actual: AuthorityScope): void;
14
+ //# sourceMappingURL=authority-scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authority-scope.d.ts","sourceRoot":"","sources":["../src/authority-scope.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,MAAM,MAAM,cAAc,GAAG,oBAAoB,GAAG,sBAAsB,CAAC;AAE3E,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,cAAc,GAAG,SAAS,GAAG,cAAc,CAyBzF;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAK/D;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,cAAc,GAAG,OAAO,CAGvF;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc,GAAG,IAAI,CAI3F"}
@@ -0,0 +1,34 @@
1
+ import { FeltDBErrorCode, FeltDBServiceError } from './error-codes.js';
2
+ export function normalizeAuthorityScope(scope) {
3
+ if (!scope) {
4
+ throw new FeltDBServiceError('An explicit tenant or platform authority scope is required', FeltDBErrorCode.AUTHORITY_SCOPE_REQUIRED);
5
+ }
6
+ if (scope.kind === 'platform')
7
+ return { kind: 'platform' };
8
+ if (scope.kind !== 'tenant' || typeof scope.tenantId !== 'string' || !scope.tenantId.trim()) {
9
+ throw new FeltDBServiceError('Tenant authority requires a non-empty tenantId', FeltDBErrorCode.AUTHORITY_SCOPE_REQUIRED);
10
+ }
11
+ if (scope.environmentId !== undefined && (typeof scope.environmentId !== 'string' || !scope.environmentId.trim())) {
12
+ throw new FeltDBServiceError('environmentId must be a non-empty string when provided', FeltDBErrorCode.AUTHORITY_SCOPE_INVALID);
13
+ }
14
+ return {
15
+ kind: 'tenant',
16
+ tenantId: scope.tenantId.trim(),
17
+ ...(scope.environmentId ? { environmentId: scope.environmentId.trim() } : {}),
18
+ };
19
+ }
20
+ export function authorityScopeKey(scope) {
21
+ const pathSafe = (value) => Array.from(new TextEncoder().encode(value), byte => byte.toString(16).padStart(2, '0')).join('');
22
+ return scope.kind === 'platform'
23
+ ? 'platform'
24
+ : `tenant/${pathSafe(scope.tenantId)}/${pathSafe(scope.environmentId ?? 'default')}`;
25
+ }
26
+ export function sameAuthorityScope(left, right) {
27
+ return left.kind === right.kind && (left.kind === 'platform'
28
+ || (right.kind === 'tenant' && left.tenantId === right.tenantId && left.environmentId === right.environmentId));
29
+ }
30
+ export function assertAuthorityScope(expected, actual) {
31
+ if (!sameAuthorityScope(expected, normalizeAuthorityScope(actual))) {
32
+ throw new FeltDBServiceError('Operation authority scope does not match the bound session', FeltDBErrorCode.AUTHORITY_SCOPE_MISMATCH);
33
+ }
34
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAIL,KAAK,mBAAmB,EACxB,KAAK,QAAQ,EACd,MAAM,gBAAgB,CAAC;AAKxB,OAAO,KAAK,EAAE,WAAW,EAAc,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAErF,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC;AAChD,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,IAAI,CAAC;AAiBjD;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,gGAAgG;IAChG,UAAU,EAAE,UAAU,GAAG,SAAS,CAAC;IACnC,4EAA4E;IAC5E,eAAe,EAAE,OAAO,CAAC;IACzB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,mCAAmC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB,CAAC,CAAC;IACtC,mCAAmC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,iEAAiE;IACjE,IAAI,CAAC,EAAE,CAAC,GAAG;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IACjC,+DAA+D;IAC/D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;GAKG;AACH,qBAAa,UAAU,CAAC,CAAC;IACvB,OAAO,CAAC,EAAE,CAAO;IACjB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,SAAS,CAA6B;IAC9C,OAAO,CAAC,KAAK,CAAW;IACxB,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,MAAM,CAA8B;IAC5C,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,oBAAoB,CAA8B;IAC1D,OAAO,CAAC,gBAAgB,CAA6B;IACrD,OAAO,CAAC,kBAAkB,CAA6B;IACvD,OAAO,CAAC,YAAY,CAAoC;IACxD,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,aAAa,CAAS;IAC9B,mFAAmF;IACnF,OAAO,CAAC,eAAe,CAAK;IAC5B,gFAAgF;IAChF,OAAO,CAAC,iBAAiB,CAAM;IAC/B,OAAO,CAAC,QAAQ,CAAoC;IACpD,oEAAoE;IACpE,OAAO,CAAC,WAAW,CAA+B;IAClD,kFAAkF;IAClF,OAAO,CAAC,eAAe,CAAuB;IAC9C;;;;;;;OAOG;IACH,OAAO,CAAC,cAAc,CAAyB;IAC/C,yEAAyE;IACzE,OAAO,CAAC,aAAa,CAA+B;gBAExC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,WAAW,UAAO;IAoBxG;;OAEG;YACW,oBAAoB;IAalC;;;OAGG;IACG,GAAG,IAAI,OAAO,CAAC,CAAC,EAAE,CAAC;IAKzB,qEAAqE;IAC/D,IAAI,IAAI,OAAO,CAAC,CAAC,EAAE,CAAC;IAI1B;;;;;;;;;;;;;OAaG;IACG,IAAI,CAAC,KAAK,GAAE,OAAO,CAAC,CAAC,CAAM,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAsChD,6EAA6E;IACvE,KAAK,CAAC,KAAK,GAAE,OAAO,CAAC,CAAC,CAAM,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAIjD;;;;;OAKG;IACH,aAAa,IAAI,mBAAmB,GAAG,IAAI;IAI3C,wEAAwE;IACxE,OAAO,CAAC,QAAQ;IAWhB;;;;;;OAMG;IACH,OAAO,CAAC,eAAe;IAiBvB;;;;;;OAMG;IACH,OAAO,CAAC,wBAAwB;IA0BhC;;;;;OAKG;IACH,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAWtC;;OAEG;IACH,WAAW,IAAI,WAAW,EAAE;IAI5B;;;OAGG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAYrC;;OAEG;IACG,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAazC;;OAEG;IACG,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAYjD;;;OAGG;IACH,KAAK,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;IAO7C;;;OAGG;IACG,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA4BrE,qEAAqE;IAC/D,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIlE;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IA+BrE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgCG;IACG,eAAe,CACnB,EAAE,EAAE,MAAM,GAAG,MAAM,EACnB,eAAe,EAAE,MAAM,EACvB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,aAAa,CAAC,EAAE,MAAM,EACtB,eAAe,CAAC,EAAE,MAAM,EACxB,gBAAgB,CAAC,EAAE,OAAO,EAC1B,YAAY,CAAC,EAAE,CAAC,GACf,OAAO,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IA2EpC;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BhD;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAK9B;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKnD;;;;;;OAMG;IACH,OAAO,CAAC,SAAS;IAKjB;;;;;;;;OAQG;YACW,6BAA6B;IAkC3C;;;;;OAKG;YACW,iCAAiC;IAyE/C;;;;;;;;;;;;;OAaG;IACG,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,CAAC,CAAA;KAAE,CAAC;IAsClG;;;OAGG;IACH,SAAS,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,IAAI;IA6CxE,mEAAmE;IACnE,KAAK,IAAI,IAAI;IASb;;;;;;;;;;;OAWG;IACH,SAAS,IAAI,OAAO,CAAC,mBAAmB,CAAC;IAIzC;;;;;;OAMG;IACH,kBAAkB,IAAI,cAAc,GAAG,IAAI;IAI3C;;;;;;;;;OASG;YACW,eAAe;IAQ7B;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CA2E/B;AAED;;GAEG;AACH,qBAAa,YAAY,CAAC,MAAM,EAAE,KAAK;IACrC,OAAO,CAAC,QAAQ,CAAO;IACvB,OAAO,CAAC,OAAO,CAAO;IACtB,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,UAAU,CAAoC;gBAGpD,QAAQ,EAAE,IAAI,EACd,OAAO,EAAE,IAAI,EACb,eAAe,EAAE,MAAM,EACvB,UAAU,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,GAAG,MAAM;IAQ/C;;OAEG;IACG,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;CAoBxD"}
1
+ {"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"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"}
@@ -130,8 +130,33 @@ export class Collection {
130
130
  * `lastQueryPlan()` reports which path ran.
131
131
  */
132
132
  async find(query = {}) {
133
- const items = await this.all();
134
133
  const entries = Object.entries(query);
134
+ if (entries.length > 0 && this.db.authority_query) {
135
+ const records = [];
136
+ let cursor;
137
+ let candidatesExamined = 0;
138
+ let indexName;
139
+ do {
140
+ const page = await this.db.authority_query({
141
+ collection: this.name,
142
+ where: entries.map(([field, eq]) => ({ field, eq })),
143
+ limit: 100,
144
+ ...(cursor ? { cursor } : {}),
145
+ });
146
+ records.push(...page.records);
147
+ candidatesExamined += page.plan?.actual_rows_scanned ?? page.records.length;
148
+ indexName = page.plan?.index ?? indexName;
149
+ cursor = page.nextCursor;
150
+ } while (cursor);
151
+ this.lastPlan = {
152
+ strategy: indexName ? 'index' : 'scan',
153
+ ...(indexName ? { indexName } : { reason: 'server reported no applicable index' }),
154
+ candidatesExamined,
155
+ recordsReturned: records.length,
156
+ };
157
+ return records;
158
+ }
159
+ const items = await this.all();
135
160
  if (entries.length === 0) {
136
161
  this.lastPlan = { strategy: 'scan', reason: 'no query fields', candidatesExamined: items.length, recordsReturned: items.length };
137
162
  return items;
@@ -1,4 +1,4 @@
1
1
  // One release train keeps generated applications installable. The repository
2
2
  // validation script checks these values against every workspace manifest.
3
- export const FELTDB_PACKAGE_VERSION = '0.8.7';
3
+ export const FELTDB_PACKAGE_VERSION = '0.9.0';
4
4
  export const feltdbPackageRange = `^${FELTDB_PACKAGE_VERSION}`;
@@ -278,7 +278,7 @@ use serde_json::Value;
278
278
  use sha2::{Digest, Sha256};
279
279
  use std::any::type_name;
280
280
  use std::collections::hash_map::DefaultHasher;
281
- use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
281
+ use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque};
282
282
  use std::fmt::{Display, Formatter};
283
283
  use std::fs::{self, OpenOptions};
284
284
  use std::hash::{Hash, Hasher};
@@ -390,6 +390,19 @@ struct Inner {
390
390
  applied_transactions: HashSet<String>,
391
391
  transaction_payload_hashes: HashMap<String, String>,
392
392
  transaction_revisions: HashMap<String, (u64, u64)>,
393
+ /// Recent commits, and who asked for them.
394
+ ///
395
+ /// Every transaction already records its actor: the audit document written
396
+ /// into the durable log carries `subject`, tenant, application and
397
+ /// revision. Until this existed that document was written and never read
398
+ /// back — replay parsed the record and discarded the audit — so the
399
+ /// database could say *what* changed and never *who changed it*.
400
+ ///
401
+ /// Bounded on purpose. This answers "who did this, recently", which is the
402
+ /// question a human asks while operating a running system; it is not an
403
+ /// audit archive, and the durable log remains the record of what happened.
404
+ /// Keeping it bounded is what makes it safe to maintain on the commit path.
405
+ transaction_attributions: VecDeque<TransactionAttribution>,
393
406
  collection_cardinality: HashMap<String, u64>,
394
407
  /// Derived equality index over `rows`. Never durable, never replicated, and
395
408
  /// never consulted for anything but candidate selection: it is maintained
@@ -737,6 +750,102 @@ pub enum JsonCasResult {
737
750
  },
738
751
  NotFound,
739
752
  }
753
+ /// How many recent commits keep their attribution in memory.
754
+ ///
755
+ /// Sized to cover what an operator reads in one sitting rather than to be a
756
+ /// log. A larger window costs memory on every instance to answer a question the
757
+ /// durable log already answers more completely.
758
+ const TRANSACTION_ATTRIBUTION_WINDOW: usize = 512;
759
+
760
+ /// How many mutated keys one attribution names before it stops listing them.
761
+ ///
762
+ /// A bulk transaction can touch thousands of records; naming them all would
763
+ /// make one commit's attribution larger than the rest of the window. The count
764
+ /// is always exact, so a truncated list is visible as a truncated list.
765
+ const TRANSACTION_ATTRIBUTION_KEYS: usize = 32;
766
+
767
+ /// Who committed a transaction, and what it touched.
768
+ ///
769
+ /// Projected from the audit document the transaction already writes. It carries
770
+ /// no record values: attribution answers *who, when, and to what*, and the
771
+ /// values are reachable through the record and revision APIs that authorize
772
+ /// each read.
773
+ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
774
+ pub struct TransactionAttribution {
775
+ pub transaction_id: String,
776
+ /// The actor the caller authenticated as, when the transaction named one.
777
+ pub subject: Option<String>,
778
+ pub tenant_id: Option<String>,
779
+ pub application_id: Option<String>,
780
+ /// The application contract revision the transaction was executed against.
781
+ pub application_revision: Option<String>,
782
+ pub base_revision: u64,
783
+ pub commit_revision: u64,
784
+ pub state_before: u64,
785
+ pub state_after: u64,
786
+ /// `capability:key` for each mutation, truncated at
787
+ /// [`TRANSACTION_ATTRIBUTION_KEYS`].
788
+ pub keys: Vec<String>,
789
+ /// How many mutations the transaction made, whether or not `keys` lists
790
+ /// them all.
791
+ pub operations: usize,
792
+ /// Milliseconds since the epoch, matching `StoredRow::unix_ms`.
793
+ pub unix_ms: u128,
794
+ }
795
+
796
+ impl TransactionAttribution {
797
+ /// Builds an attribution from a transaction record's own audit document.
798
+ fn from_record(
799
+ transaction_id: &str,
800
+ audit: Option<&Value>,
801
+ base_revision: u64,
802
+ commit_revision: u64,
803
+ state_before: u64,
804
+ state_after: u64,
805
+ rows: &[StoredRow],
806
+ unix_ms: u128,
807
+ ) -> Self {
808
+ let text = |field: &str| {
809
+ audit
810
+ .and_then(|value| value.get(field))
811
+ .and_then(Value::as_str)
812
+ .map(str::to_string)
813
+ };
814
+ TransactionAttribution {
815
+ transaction_id: transaction_id.to_string(),
816
+ subject: text("subject"),
817
+ tenant_id: text("tenant"),
818
+ application_id: text("application"),
819
+ application_revision: text("revision"),
820
+ base_revision,
821
+ commit_revision,
822
+ state_before,
823
+ state_after,
824
+ keys: rows
825
+ .iter()
826
+ .take(TRANSACTION_ATTRIBUTION_KEYS)
827
+ .map(|row| format!("{}:{}", row.capability, row.key))
828
+ .collect(),
829
+ operations: rows.len(),
830
+ unix_ms,
831
+ }
832
+ }
833
+
834
+ /// Whether this transaction touched a record, as far as `keys` records it.
835
+ pub fn touched(&self, capability: &str, key: &str) -> bool {
836
+ let target = format!("{capability}:{key}");
837
+ self.keys.iter().any(|held| *held == target)
838
+ }
839
+ }
840
+
841
+ /// Records an attribution, discarding the oldest once the window is full.
842
+ fn remember_attribution(inner: &mut Inner, attribution: TransactionAttribution) {
843
+ if inner.transaction_attributions.len() >= TRANSACTION_ATTRIBUTION_WINDOW {
844
+ inner.transaction_attributions.pop_front();
845
+ }
846
+ inner.transaction_attributions.push_back(attribution);
847
+ }
848
+
740
849
  #[derive(Debug, Clone, Serialize, Deserialize)]
741
850
  struct TransactionLogRecord {
742
851
  record_type: String,
@@ -2827,6 +2936,22 @@ impl FeltDb {
2827
2936
  bookkeeping_nanos += began.elapsed().as_nanos().min(u64::MAX as u128) as u64;
2828
2937
  }
2829
2938
  append_transaction(&inner.path, &record)?;
2939
+ // Retained from the record that was just made durable, so the
2940
+ // in-memory answer to "who committed this" cannot describe a
2941
+ // transaction the log does not hold.
2942
+ remember_attribution(
2943
+ &mut inner,
2944
+ TransactionAttribution::from_record(
2945
+ transaction_id,
2946
+ record.audit.as_ref(),
2947
+ base_revision,
2948
+ base_revision + 1,
2949
+ state_before,
2950
+ next_sequence,
2951
+ &record.rows,
2952
+ now_ms(),
2953
+ ),
2954
+ );
2830
2955
  inner.authority_revision = base_revision + 1;
2831
2956
  inner.sequence = next_sequence;
2832
2957
  for row in &rows {
@@ -3809,6 +3934,71 @@ impl FeltDb {
3809
3934
  }
3810
3935
 
3811
3936
  /// Earliest operation still available for incremental replay, by origin.
3937
+ /// Recent commits and who made them, newest first.
3938
+ ///
3939
+ /// Bounded by [`TRANSACTION_ATTRIBUTION_WINDOW`]. A transaction older than
3940
+ /// the window is not absent from history — it is in the durable log — it is
3941
+ /// simply no longer answerable from memory, and callers that present this
3942
+ /// to a human must say which of the two they are showing.
3943
+ pub fn recent_transactions(&self, limit: usize) -> Result<Vec<TransactionAttribution>> {
3944
+ let inner = self.state();
3945
+ Ok(inner
3946
+ .transaction_attributions
3947
+ .iter()
3948
+ .rev()
3949
+ .take(limit)
3950
+ .cloned()
3951
+ .collect())
3952
+ }
3953
+
3954
+ /// One transaction's attribution, if it is still inside the window.
3955
+ pub fn transaction_attribution(
3956
+ &self,
3957
+ transaction_id: &str,
3958
+ ) -> Result<Option<TransactionAttribution>> {
3959
+ let inner = self.state();
3960
+ Ok(inner
3961
+ .transaction_attributions
3962
+ .iter()
3963
+ .rev()
3964
+ .find(|attribution| attribution.transaction_id == transaction_id)
3965
+ .cloned())
3966
+ }
3967
+
3968
+ /// Recent commits that named this record, newest first.
3969
+ ///
3970
+ /// This is how "who last changed this record" is answered without scanning
3971
+ /// the durable log: a transaction names the keys it mutated, so the join is
3972
+ /// over data the commit already recorded.
3973
+ pub fn transactions_touching(
3974
+ &self,
3975
+ capability: &str,
3976
+ key: &str,
3977
+ limit: usize,
3978
+ ) -> Result<Vec<TransactionAttribution>> {
3979
+ let inner = self.state();
3980
+ Ok(inner
3981
+ .transaction_attributions
3982
+ .iter()
3983
+ .rev()
3984
+ .filter(|attribution| attribution.touched(capability, key))
3985
+ .take(limit)
3986
+ .cloned()
3987
+ .collect())
3988
+ }
3989
+
3990
+ /// How much of the attribution window is in use, and what it can hold.
3991
+ ///
3992
+ /// Reported so a reader can tell an idle system from a window that has
3993
+ /// already rolled over.
3994
+ pub fn transaction_attribution_window(&self) -> Result<(usize, usize)> {
3995
+ let inner = self.state();
3996
+ Ok((
3997
+ inner.transaction_attributions.len(),
3998
+ TRANSACTION_ATTRIBUTION_WINDOW,
3999
+ ))
4000
+ }
4001
+
3812
4002
  pub fn retained_operation_floors(&self) -> Result<HashMap<String, u64>> {
3813
4003
  let inner = self.state();
3814
4004
  Ok(inner.change_log.retained_from())
@@ -4693,6 +4883,24 @@ fn replay_log(inner: &mut Inner) -> Result<()> {
4693
4883
  .transaction_payload_hashes
4694
4884
  .insert(transaction.transaction_id.clone(), hash);
4695
4885
  }
4886
+ // Attribution is restored on replay for the same reason it is kept
4887
+ // on commit: an operator who restarts a server must not lose the
4888
+ // answer to "who did this" for work the server already holds.
4889
+ let attribution = TransactionAttribution::from_record(
4890
+ &transaction.transaction_id,
4891
+ transaction.audit.as_ref(),
4892
+ base_revision,
4893
+ inner.authority_revision,
4894
+ transaction.state_before,
4895
+ transaction.state_after,
4896
+ &transaction.rows,
4897
+ transaction
4898
+ .rows
4899
+ .first()
4900
+ .map(|row| row.unix_ms)
4901
+ .unwrap_or_default(),
4902
+ );
4903
+ remember_attribution(inner, attribution);
4696
4904
  if inner
4697
4905
  .applied_transactions
4698
4906
  .insert(transaction.transaction_id)
@@ -4987,6 +5195,140 @@ mod tests {
4987
5195
  ))
4988
5196
  }
4989
5197
 
5198
+ /// A commit is attributable: the audit document a transaction already
5199
+ /// writes is retained, so a human can ask who made a change.
5200
+ #[test]
5201
+ fn a_committed_transaction_records_who_committed_it() {
5202
+ let path = temp_file();
5203
+ let db = open(&path).expect("open");
5204
+ db.apply_atomic_transaction(
5205
+ "tx_agent_1",
5206
+ None,
5207
+ &[],
5208
+ &[AtomicMutation {
5209
+ capability: "billing:Customer".into(),
5210
+ key: "cus_1".into(),
5211
+ value: Some(serde_json::json!({"name": "Ada", "status": "active"})),
5212
+ }],
5213
+ Some(serde_json::json!({
5214
+ "subject": "agent_data_cleanup",
5215
+ "tenant": "acme",
5216
+ "application": "app_billing",
5217
+ "revision": "rev_7",
5218
+ })),
5219
+ )
5220
+ .expect("commit");
5221
+
5222
+ let recent = db.recent_transactions(10).expect("attributions");
5223
+ assert_eq!(recent.len(), 1);
5224
+ assert_eq!(recent[0].transaction_id, "tx_agent_1");
5225
+ assert_eq!(recent[0].subject.as_deref(), Some("agent_data_cleanup"));
5226
+ assert_eq!(recent[0].application_id.as_deref(), Some("app_billing"));
5227
+ assert_eq!(recent[0].operations, 1);
5228
+ assert_eq!(recent[0].keys, vec!["billing:Customer:cus_1".to_string()]);
5229
+ // Attribution says who and what, never what was written.
5230
+ assert!(!serde_json::to_string(&recent[0]).unwrap().contains("Ada"));
5231
+
5232
+ let touching = db
5233
+ .transactions_touching("billing:Customer", "cus_1", 10)
5234
+ .expect("touching");
5235
+ assert_eq!(touching.len(), 1);
5236
+ assert_eq!(touching[0].transaction_id, "tx_agent_1");
5237
+ assert!(db
5238
+ .transactions_touching("billing:Customer", "cus_2", 10)
5239
+ .expect("touching")
5240
+ .is_empty());
5241
+
5242
+ assert_eq!(
5243
+ db.transaction_attribution("tx_agent_1")
5244
+ .expect("lookup")
5245
+ .map(|value| value.subject)
5246
+ .flatten()
5247
+ .as_deref(),
5248
+ Some("agent_data_cleanup")
5249
+ );
5250
+
5251
+ // Attribution survives a restart, because it is rebuilt from the same
5252
+ // durable records the rest of recovery replays.
5253
+ drop(db);
5254
+ let reopened = open(&path).expect("reopen");
5255
+ let after = reopened.recent_transactions(10).expect("attributions");
5256
+ assert_eq!(after.len(), 1);
5257
+ assert_eq!(after[0].subject.as_deref(), Some("agent_data_cleanup"));
5258
+ assert_eq!(after[0].keys, vec!["billing:Customer:cus_1".to_string()]);
5259
+ drop(reopened);
5260
+ let _ = fs::remove_file(path);
5261
+ }
5262
+
5263
+ /// The window is bounded, and reports that it is.
5264
+ #[test]
5265
+ fn transaction_attribution_is_bounded_and_newest_first() {
5266
+ let path = temp_file();
5267
+ let db = open(&path).expect("open");
5268
+ let commits = TRANSACTION_ATTRIBUTION_WINDOW + 5;
5269
+ for index in 0..commits {
5270
+ db.apply_atomic_transaction(
5271
+ &format!("tx_{index}"),
5272
+ None,
5273
+ &[],
5274
+ &[AtomicMutation {
5275
+ capability: "app:Note".into(),
5276
+ key: format!("note_{index}"),
5277
+ value: Some(serde_json::json!({"index": index})),
5278
+ }],
5279
+ Some(serde_json::json!({"subject": format!("actor_{index}")})),
5280
+ )
5281
+ .expect("commit");
5282
+ }
5283
+ let (held, capacity) = db.transaction_attribution_window().expect("window");
5284
+ assert_eq!(capacity, TRANSACTION_ATTRIBUTION_WINDOW);
5285
+ assert_eq!(held, TRANSACTION_ATTRIBUTION_WINDOW);
5286
+ let recent = db.recent_transactions(3).expect("attributions");
5287
+ assert_eq!(
5288
+ recent.iter().map(|value| value.transaction_id.clone()).collect::<Vec<_>>(),
5289
+ vec![
5290
+ format!("tx_{}", commits - 1),
5291
+ format!("tx_{}", commits - 2),
5292
+ format!("tx_{}", commits - 3),
5293
+ ]
5294
+ );
5295
+ // The oldest commits rolled out of the window rather than being kept
5296
+ // forever; the durable log still holds them.
5297
+ assert!(db.transaction_attribution("tx_0").expect("lookup").is_none());
5298
+ drop(db);
5299
+ let _ = fs::remove_file(path);
5300
+ }
5301
+
5302
+ /// A bulk transaction names a bounded sample of keys and an exact count.
5303
+ #[test]
5304
+ fn a_bulk_transaction_truncates_its_key_list_and_keeps_the_count() {
5305
+ let path = temp_file();
5306
+ let db = open(&path).expect("open");
5307
+ let mutations = (0..TRANSACTION_ATTRIBUTION_KEYS + 20)
5308
+ .map(|index| AtomicMutation {
5309
+ capability: "app:Customer".into(),
5310
+ key: format!("cus_{index}"),
5311
+ value: Some(serde_json::json!({"index": index})),
5312
+ })
5313
+ .collect::<Vec<_>>();
5314
+ db.apply_atomic_transaction(
5315
+ "tx_bulk",
5316
+ None,
5317
+ &[],
5318
+ &mutations,
5319
+ Some(serde_json::json!({"subject": "migration_agent"})),
5320
+ )
5321
+ .expect("commit");
5322
+ let attribution = db
5323
+ .transaction_attribution("tx_bulk")
5324
+ .expect("lookup")
5325
+ .expect("held");
5326
+ assert_eq!(attribution.operations, mutations.len());
5327
+ assert_eq!(attribution.keys.len(), TRANSACTION_ATTRIBUTION_KEYS);
5328
+ drop(db);
5329
+ let _ = fs::remove_file(path);
5330
+ }
5331
+
4990
5332
  #[test]
4991
5333
  fn open_is_zero_init_and_creates_store() {
4992
5334
  let path = temp_file();