@feltdb/core 0.8.1 → 0.8.3

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 (28) hide show
  1. package/dist/create/package-versions.js +1 -1
  2. package/dist/create/server-source/crates/feltdb/src/application.rs +19 -0
  3. package/dist/create/server-source/crates/feltdb/src/authority.rs +349 -0
  4. package/dist/create/server-source/crates/feltdb/src/bin/feltdb-authority-client.rs +32 -0
  5. package/dist/create/server-source/crates/feltdb/src/bin/feltdb-authority.rs +19 -0
  6. package/dist/create/server-source/crates/feltdb/src/lib.rs +444 -189
  7. package/dist/create/server-source/crates/feltdb/src/state_contract.rs +244 -51
  8. package/dist/create/server-source/crates/feltdb/tests/authority_process.rs +297 -0
  9. package/dist/create/server-source/crates/feltdb-server/src/auth.rs +41 -11
  10. package/dist/create/server-source/crates/feltdb-server/src/key_management.rs +8 -3
  11. package/dist/create/server-source/crates/feltdb-server/src/main.rs +1164 -225
  12. package/dist/create/server-source/crates/feltdb-server/src/tenancy.rs +134 -0
  13. package/dist/file-db.d.ts +8 -15
  14. package/dist/file-db.d.ts.map +1 -1
  15. package/dist/file-db.js +234 -130
  16. package/dist/http-db.d.ts +3 -0
  17. package/dist/http-db.d.ts.map +1 -1
  18. package/dist/http-db.js +2 -1
  19. package/dist/state-contract.d.ts +4 -1
  20. package/dist/state-contract.d.ts.map +1 -1
  21. package/dist/state-contract.js +1 -1
  22. package/dist/studio-app/assets/{feltdb_wasm-bIqcRzAr.js → feltdb_wasm-DB8cX151.js} +1 -1
  23. package/dist/studio-app/assets/feltdb_wasm_bg-ClhDHp0S.wasm +0 -0
  24. package/dist/studio-app/assets/{index-XGYdlElN.js → index-B0k4UAlI.js} +1 -1
  25. package/dist/studio-app/index.html +1 -1
  26. package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
  27. package/package.json +1 -1
  28. package/dist/studio-app/assets/feltdb_wasm_bg-BE79okwX.wasm +0 -0
@@ -41,6 +41,41 @@ pub struct Application {
41
41
  pub active_revision: Option<String>,
42
42
  #[serde(default)]
43
43
  pub environment_revisions: std::collections::BTreeMap<String, String>,
44
+ #[serde(default)]
45
+ pub qualification: Option<QualificationMarker>,
46
+ }
47
+ #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
48
+ pub struct QualificationMarker {
49
+ pub purpose: String,
50
+ pub disposable: bool,
51
+ pub environment: String,
52
+ pub proof_run_id: String,
53
+ pub expires_at: u64,
54
+ }
55
+ impl QualificationMarker {
56
+ fn valid_for_creation(&self, timestamp: u64) -> bool {
57
+ self.purpose == "qualification"
58
+ && self.disposable
59
+ && self.environment == "qualification"
60
+ && !self.proof_run_id.trim().is_empty()
61
+ && self.expires_at > timestamp
62
+ }
63
+
64
+ fn authorize_destruction(&self, proof_run_id: &str, timestamp: u64) -> Result<(), String> {
65
+ if self.purpose != "qualification" || !self.disposable {
66
+ return Err("refusing destructive qualification: application is not marked disposable".into());
67
+ }
68
+ if self.environment != "qualification" {
69
+ return Err("refusing destructive qualification: environment is not qualification".into());
70
+ }
71
+ if self.proof_run_id.trim().is_empty() || proof_run_id.trim().is_empty() || self.proof_run_id != proof_run_id {
72
+ return Err("refusing destructive qualification: proof run does not match".into());
73
+ }
74
+ if self.expires_at <= timestamp {
75
+ return Err("refusing destructive qualification: disposable marker has expired".into());
76
+ }
77
+ Ok(())
78
+ }
44
79
  }
45
80
  #[derive(Debug, Clone, Serialize, Deserialize)]
46
81
  pub struct TenantMembership {
@@ -765,10 +800,25 @@ impl TenancyStore {
765
800
  actor: &str,
766
801
  tenant_id: &str,
767
802
  name: &str,
803
+ ) -> Result<Application, String> {
804
+ self.create_application_with_qualification(actor, tenant_id, name, None)
805
+ }
806
+
807
+ pub fn create_application_with_qualification(
808
+ &self,
809
+ actor: &str,
810
+ tenant_id: &str,
811
+ name: &str,
812
+ qualification: Option<QualificationMarker>,
768
813
  ) -> Result<Application, String> {
769
814
  if !self.authorize_tenant(actor, tenant_id, "applications:write") {
770
815
  return Err("forbidden".into());
771
816
  }
817
+ if let Some(marker) = qualification.as_ref() {
818
+ if !marker.valid_for_creation(now()) {
819
+ return Err("invalid disposable qualification marker".into());
820
+ }
821
+ }
772
822
  let mut records = self
773
823
  .records
774
824
  .write()
@@ -794,6 +844,7 @@ impl TenancyStore {
794
844
  status: application_status(),
795
845
  active_revision: None,
796
846
  environment_revisions: Default::default(),
847
+ qualification,
797
848
  };
798
849
  records.application_memberships.push(ApplicationMembership {
799
850
  application_id: application.id.clone(),
@@ -816,6 +867,22 @@ impl TenancyStore {
816
867
  Ok(application)
817
868
  }
818
869
 
870
+ pub fn delete_qualification_application(
871
+ &self,
872
+ actor: &str,
873
+ application_id: &str,
874
+ proof_run_id: &str,
875
+ ) -> Result<(), String> {
876
+ let application = self
877
+ .application_for(actor, application_id)
878
+ .ok_or_else(|| "application not found".to_string())?;
879
+ let marker = application.qualification.ok_or_else(|| {
880
+ "refusing destructive qualification: application is not marked disposable".to_string()
881
+ })?;
882
+ marker.authorize_destruction(proof_run_id, now())?;
883
+ self.delete_application(actor, application_id)
884
+ }
885
+
819
886
  pub fn delete_application(&self, actor: &str, application_id: &str) -> Result<(), String> {
820
887
  let tenant_id = self
821
888
  .application_tenant(application_id)
@@ -1238,6 +1305,73 @@ mod tests {
1238
1305
  assert!(!store.authorize_application("user_a", &tenant_a.id, &app_b.id, "state:read"));
1239
1306
  }
1240
1307
 
1308
+ #[test]
1309
+ fn destructive_qualification_requires_an_unexpired_matching_marker() {
1310
+ let valid_marker = QualificationMarker {
1311
+ purpose: "qualification".into(), disposable: true, environment: "qualification".into(), proof_run_id: "proof-1".into(), expires_at: now() + 300,
1312
+ };
1313
+ assert!(valid_marker.authorize_destruction("", now()).unwrap_err().contains("does not match"));
1314
+ assert!(valid_marker.authorize_destruction("proof-other", now()).unwrap_err().contains("does not match"));
1315
+ let mut wrong_environment = valid_marker.clone();
1316
+ wrong_environment.environment = "production".into();
1317
+ assert!(wrong_environment.authorize_destruction("proof-1", now()).unwrap_err().contains("environment"));
1318
+ let mut not_disposable = valid_marker.clone();
1319
+ not_disposable.disposable = false;
1320
+ assert!(not_disposable.authorize_destruction("proof-1", now()).unwrap_err().contains("not marked disposable"));
1321
+ let mut expired = valid_marker.clone();
1322
+ expired.expires_at = now().saturating_sub(1);
1323
+ assert!(expired.authorize_destruction("proof-1", now()).unwrap_err().contains("expired"));
1324
+ assert!(valid_marker.authorize_destruction("proof-1", now()).is_ok());
1325
+
1326
+ let store = store("qualification-guard");
1327
+ let tenant = store.create_tenant("owner", "Qualification").unwrap();
1328
+ let normal = store
1329
+ .create_application("owner", &tenant.id, "Normal")
1330
+ .unwrap();
1331
+ assert!(store
1332
+ .delete_qualification_application("owner", &normal.id, "proof-1")
1333
+ .unwrap_err()
1334
+ .contains("not marked disposable"));
1335
+ assert!(store
1336
+ .create_application_with_qualification(
1337
+ "owner",
1338
+ &tenant.id,
1339
+ "Expired",
1340
+ Some(QualificationMarker {
1341
+ purpose: "qualification".into(),
1342
+ disposable: true,
1343
+ environment: "qualification".into(),
1344
+ proof_run_id: "proof-expired".into(),
1345
+ expires_at: now().saturating_sub(1),
1346
+ }),
1347
+ )
1348
+ .unwrap_err()
1349
+ .contains("invalid disposable"));
1350
+
1351
+ let proof = store
1352
+ .create_application_with_qualification(
1353
+ "owner",
1354
+ &tenant.id,
1355
+ "Proof",
1356
+ Some(QualificationMarker {
1357
+ purpose: "qualification".into(),
1358
+ disposable: true,
1359
+ environment: "qualification".into(),
1360
+ proof_run_id: "proof-1".into(),
1361
+ expires_at: now() + 300,
1362
+ }),
1363
+ )
1364
+ .unwrap();
1365
+ assert!(store
1366
+ .delete_qualification_application("owner", &proof.id, "proof-other")
1367
+ .unwrap_err()
1368
+ .contains("does not match"));
1369
+ store
1370
+ .delete_qualification_application("owner", &proof.id, "proof-1")
1371
+ .unwrap();
1372
+ assert!(store.application_for("owner", &proof.id).is_none());
1373
+ }
1374
+
1241
1375
  #[test]
1242
1376
  fn invitation_acceptance_and_revocation_change_authorization() {
1243
1377
  let store = store("invitation");
package/dist/file-db.d.ts CHANGED
@@ -25,6 +25,7 @@ export declare class FileJsDb implements JsDb {
25
25
  private readonly origin;
26
26
  private readonly peers;
27
27
  private lockFd;
28
+ private lockToken;
28
29
  private readonly applied;
29
30
  constructor(path: string);
30
31
  private loadState;
@@ -34,6 +35,10 @@ export declare class FileJsDb implements JsDb {
34
35
  private cleanupStaleLock;
35
36
  private acquireLockAsync;
36
37
  private releaseLockAsync;
38
+ private ownsLock;
39
+ private acquireLockSync;
40
+ private withMutationLock;
41
+ private refresh;
37
42
  private saveState;
38
43
  private event;
39
44
  /**
@@ -64,21 +69,9 @@ export declare class FileJsDb implements JsDb {
64
69
  instance_id(): string;
65
70
  get_sequence(): number;
66
71
  /**
67
- * The file runtime declines revision validation, and this is the honest answer
68
- * rather than a missing feature.
69
- *
70
- * A shared directory has no serialization authority over its writers.
71
- * `insert`, `update`, `delete`, and `commit_transaction` do not take the
72
- * cross-process lock, and publication is a whole-snapshot rename, so two
73
- * processes writing the same directory lose data. `state.sequence` therefore
74
- * cannot distinguish "one writer committed 50" from "two writers committed 50
75
- * each and one snapshot won" — it produces a plausible but false answer,
76
- * which is worse than producing none.
77
- *
78
- * Measured in tools/cli/test/file-runtime-concurrency.test.mjs and analysed in
79
- * docs/architecture/freshness-revision-investigation.md. Making this runtime
80
- * revision-capable requires fixing lock discipline and publication together;
81
- * advertising `refresh` is correct until then, not a placeholder.
72
+ * The file runtime uses explicit refresh validation. Cross-process mutations
73
+ * are serialized and reads reload the committed snapshot, but the public read
74
+ * contract does not yet return a scoped revision alongside its value.
82
75
  */
83
76
  freshness(): FreshnessCapability;
84
77
  audit_events(): PortableEvent[];
@@ -1 +1 @@
1
- {"version":3,"file":"file-db.d.ts","sourceRoot":"","sources":["../src/file-db.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,KAAK,EACV,uBAAuB,EACvB,wBAAwB,EACxB,wBAAwB,EACxB,yBAAyB,EAG1B,MAAM,0BAA0B,CAAC;AAYlC,OAAO,KAAK,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAkB1F,UAAU,QAAQ;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,aAAa;IAAG,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,KAAK,GAAG,QAAQ,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE;AA4BrK,qBAAa,QAAS,YAAW,IAAI;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAC3C,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;gBAEjC,IAAI,EAAE,MAAM;IAmCxB,OAAO,CAAC,SAAS;IAkCjB,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,WAAW;IAuBnB,OAAO,CAAC,gBAAgB;YAWV,gBAAgB;IA6B9B,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,SAAS;IAsDjB,OAAO,CAAC,KAAK;IAeb;;;;;;;;;;OAUG;IACG,kBAAkB,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,uBAAuB,CAAC;IA0D7F,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,QAAQ;IAa5C,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,QAAQ;IAO5C,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ;IAO1B,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ;IAW7B,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ;IAQnC,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ;IAIpD,UAAU,IAAI,QAAQ;IAItB,SAAS,IAAI,QAAQ;IAiBrB,QAAQ,IAAI,QAAQ;IAIpB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ;IAKvC,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ;IAK1C,2BAA2B,IAAI,QAAQ;IAIvC,oBAAoB,IAAI,QAAQ;IAIhC,WAAW,IAAI,MAAM;IAIrB,YAAY,IAAI,MAAM;IAItB;;;;;;;;;;;;;;;;OAgBG;IACH,SAAS,IAAI,mBAAmB;IAUhC,YAAY;IAIZ,iBAAiB,CAAC,aAAa,EAAE,MAAM;IAIvC,uBAAuB,CAAC,UAAU,EAAE,aAAa,EAAE;;;;IA0C7C,gBAAgB,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAS7E,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAwCtF,cAAc,CAAC,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,wBAAwB,CAAC;IA2DjF,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,yBAAyB,CAAC;IA0FxF,GAAG,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IA0F7P,KAAK,IAAI,IAAI;CAId"}
1
+ {"version":3,"file":"file-db.d.ts","sourceRoot":"","sources":["../src/file-db.ts"],"names":[],"mappings":"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"}