create-feltdb 0.9.0 → 0.10.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 (29) hide show
  1. package/dist/package-versions.js +1 -1
  2. package/dist/server-source/crates/feltdb/src/acceptance_tests.rs +8 -1
  3. package/dist/server-source/crates/feltdb/src/application.rs +16 -0
  4. package/dist/server-source/crates/feltdb/src/bin/feltdb_node.rs +21 -22
  5. package/dist/server-source/crates/feltdb/src/lib.rs +451 -33
  6. package/dist/server-source/crates/feltdb/src/state_contract.rs +48 -3
  7. package/dist/server-source/crates/feltdb/src/state_model.rs +569 -4
  8. package/dist/server-source/crates/feltdb/src/state_trigger.rs +1 -0
  9. package/dist/server-source/crates/feltdb/src/versioned_object_substrate.rs +708 -0
  10. package/dist/server-source/crates/feltdb/src/worker_mesh.rs +1 -0
  11. package/dist/server-source/crates/feltdb/src/workload.rs +480 -44
  12. package/dist/server-source/crates/feltdb/tests/generalized_version_control_evidence.rs +123 -0
  13. package/dist/server-source/crates/feltdb/tests/generalized_version_control_substrate_completion.rs +281 -0
  14. package/dist/server-source/crates/feltdb/tests/production_readiness_contract.rs +15 -3
  15. package/dist/server-source/crates/feltdb/tests/revision_retention_boundary_evidence.rs +28 -18
  16. package/dist/server-source/crates/feltdb/tests/storage_independent_versioned_object_substrate.rs +328 -0
  17. package/dist/server-source/crates/feltdb/tests/workload_envelope_contract.rs +20 -3
  18. package/dist/server-source/crates/feltdb-server/src/app_state.rs +14 -4
  19. package/dist/server-source/crates/feltdb-server/src/backup.rs +0 -4
  20. package/dist/server-source/crates/feltdb-server/src/canonical_principals.rs +1093 -0
  21. package/dist/server-source/crates/feltdb-server/src/causal.rs +905 -144
  22. package/dist/server-source/crates/feltdb-server/src/connections.rs +9 -6
  23. package/dist/server-source/crates/feltdb-server/src/control_plane.rs +90 -0
  24. package/dist/server-source/crates/feltdb-server/src/lib.rs +1 -0
  25. package/dist/server-source/crates/feltdb-server/src/main.rs +443 -77
  26. package/dist/server-source/crates/feltdb-server/src/principals.rs +9 -501
  27. package/dist/server-source/crates/feltdb-server/src/tenancy.rs +22 -0
  28. package/dist/server-source/crates/feltdb-server/tests/principal_tenant_migration.rs +247 -0
  29. package/package.json +1 -1
@@ -0,0 +1,247 @@
1
+ use feltdb::{AtomicMutation, AtomicPrecondition, FeltDb};
2
+ use feltdb_server::canonical_principals::{
3
+ application_ownership_key, CanonicalApplicationOwnership,
4
+ };
5
+ use serde_json::json;
6
+ use std::{
7
+ fs,
8
+ io::{BufRead, BufReader},
9
+ path::{Path, PathBuf},
10
+ process::{Child, Command, Stdio},
11
+ time::{SystemTime, UNIX_EPOCH},
12
+ };
13
+
14
+ const PRODUCTION_SHAPED_APPLICATION: &str = "app_pKEN4gJ0lvQV4D2ZWa";
15
+
16
+ fn fixture_dir() -> PathBuf {
17
+ std::env::temp_dir().join(format!(
18
+ "feltdb-principal-tenant-migration-{}-{}",
19
+ std::process::id(),
20
+ SystemTime::now()
21
+ .duration_since(UNIX_EPOCH)
22
+ .unwrap()
23
+ .as_nanos()
24
+ ))
25
+ }
26
+
27
+ fn start(data: &Path) -> Child {
28
+ Command::new(env!("CARGO_BIN_EXE_feltdb-server"))
29
+ .args([
30
+ "--host",
31
+ "127.0.0.1",
32
+ "--port",
33
+ "0",
34
+ "--namespace",
35
+ "principal-tenant-migration",
36
+ "--data",
37
+ data.to_str().unwrap(),
38
+ "--keys",
39
+ data.with_extension("keys.json").to_str().unwrap(),
40
+ "--audit",
41
+ data.with_extension("audit.log").to_str().unwrap(),
42
+ ])
43
+ .env(
44
+ "FELTDB_MASTER_KEY",
45
+ "principal-tenant-migration-development-key",
46
+ )
47
+ .stdout(Stdio::piped())
48
+ .stderr(Stdio::piped())
49
+ .spawn()
50
+ .unwrap()
51
+ }
52
+
53
+ fn await_readiness(child: &mut Child) {
54
+ let stdout = child.stdout.take().unwrap();
55
+ for line in BufReader::new(stdout).lines() {
56
+ let line = line.unwrap();
57
+ if line.contains("ready at http://127.0.0.1:") {
58
+ return;
59
+ }
60
+ }
61
+ let stderr = child
62
+ .stderr
63
+ .take()
64
+ .map(|value| std::io::read_to_string(value).unwrap_or_default())
65
+ .unwrap_or_default();
66
+ panic!("server failed before readiness: {stderr}");
67
+ }
68
+
69
+ fn await_failure(child: Child) -> String {
70
+ let output = child.wait_with_output().unwrap();
71
+ assert!(!output.status.success());
72
+ format!(
73
+ "{}{}",
74
+ String::from_utf8_lossy(&output.stdout),
75
+ String::from_utf8_lossy(&output.stderr)
76
+ )
77
+ }
78
+
79
+ #[test]
80
+ fn principal_startup_production_shaped_fixture_and_authority_loss() {
81
+ let directory = fixture_dir();
82
+ fs::create_dir_all(&directory).unwrap();
83
+ let data = directory.join("feltdb.log");
84
+ fs::write(
85
+ data.with_extension("platform.json"),
86
+ serde_json::to_vec_pretty(&json!({
87
+ "users": [],
88
+ "tenants": [{
89
+ "id": "tenant_production",
90
+ "name": "Sanitized production tenant",
91
+ "slug": "sanitized-production-tenant",
92
+ "created_by": "principal_owner",
93
+ "created_at": 1,
94
+ "updated_at": 1
95
+ }],
96
+ "applications": [],
97
+ "tenant_memberships": [],
98
+ "application_memberships": [],
99
+ "application_service_principals": [],
100
+ "invitations": [],
101
+ "audit": []
102
+ }))
103
+ .unwrap(),
104
+ )
105
+ .unwrap();
106
+ let legacy = data.with_extension("principals.json");
107
+ fs::write(
108
+ &legacy,
109
+ serde_json::to_vec_pretty(&json!({
110
+ "principals": [{
111
+ "id": "agent_stable",
112
+ "application_id": PRODUCTION_SHAPED_APPLICATION,
113
+ "kind": "agent",
114
+ "name": "Sanitized Agent",
115
+ "version": 3,
116
+ "status": "active",
117
+ "created_by": "principal_delegator",
118
+ "created_at": 100,
119
+ "capabilities": ["state:read"],
120
+ "parent_id": null,
121
+ "worker_id": null,
122
+ "started_at": null,
123
+ "completed_at": null
124
+ }],
125
+ "audit": []
126
+ }))
127
+ .unwrap(),
128
+ )
129
+ .unwrap();
130
+
131
+ let pre_backfill = start(&data);
132
+ let output = await_failure(pre_backfill);
133
+ assert!(output
134
+ .contains("PRINCIPAL_TENANT_MIGRATION_CONFLICT:MISSING_OWNERSHIP:app_pKEN4gJ0lvQV4D2ZWa"));
135
+
136
+ let db = FeltDb::open(&data).unwrap();
137
+ let ownership = CanonicalApplicationOwnership {
138
+ application_id: PRODUCTION_SHAPED_APPLICATION.into(),
139
+ tenant_id: "tenant_production".into(),
140
+ source_authority: "sanitized_authoritative_registration_export".into(),
141
+ source_record_id: "registration_record_1".into(),
142
+ source_digest: format!("sha256:{}", "a".repeat(64)),
143
+ authorization_reference: "approval://release/0.10D-R2/test".into(),
144
+ authorized_by: "release_test_authority".into(),
145
+ authorized_at: "2026-09-09T00:00:00Z".into(),
146
+ migration_version: "application-ownership-backfill/v1".into(),
147
+ reason: "sanitized production incident regression".into(),
148
+ };
149
+ let transaction_id = "application-ownership-backfill-test-v1";
150
+ let commit = || {
151
+ db.apply_atomic_transaction(
152
+ transaction_id,
153
+ None,
154
+ &[AtomicPrecondition {
155
+ capability: "system_application_ownership".into(),
156
+ key: application_ownership_key(PRODUCTION_SHAPED_APPLICATION),
157
+ expected_version: None,
158
+ }],
159
+ &[AtomicMutation {
160
+ capability: "system_application_ownership".into(),
161
+ key: application_ownership_key(PRODUCTION_SHAPED_APPLICATION),
162
+ value: Some(serde_json::to_value(&ownership).unwrap()),
163
+ }],
164
+ Some(json!({
165
+ "kind": "application_ownership_backfill",
166
+ "tenant": "tenant_production",
167
+ "application": PRODUCTION_SHAPED_APPLICATION,
168
+ "authorization_reference": ownership.authorization_reference,
169
+ "source_digest": ownership.source_digest,
170
+ "operation_id": transaction_id
171
+ })),
172
+ )
173
+ };
174
+ let first_commit = commit().unwrap();
175
+ assert!(!first_commit.duplicate);
176
+ let repeated = commit().unwrap();
177
+ assert!(repeated.duplicate);
178
+ assert_eq!(repeated.commit_revision, first_commit.commit_revision);
179
+ let attribution = db
180
+ .transaction_attribution(transaction_id)
181
+ .unwrap()
182
+ .expect("durable ownership transaction attribution");
183
+ assert_eq!(attribution.transaction_id, transaction_id);
184
+ assert!(!attribution.operation_ids.is_empty());
185
+ drop(db);
186
+
187
+ let mut first = start(&data);
188
+ await_readiness(&mut first);
189
+ first.kill().unwrap();
190
+ first.wait().unwrap();
191
+
192
+ let reopened = FeltDb::open(&data).unwrap();
193
+ assert_eq!(
194
+ reopened
195
+ .get_value(&application_ownership_key(PRODUCTION_SHAPED_APPLICATION))
196
+ .unwrap()
197
+ .unwrap()["tenant_id"],
198
+ "tenant_production"
199
+ );
200
+ assert!(reopened
201
+ .transaction_attribution(transaction_id)
202
+ .unwrap()
203
+ .is_some());
204
+ drop(reopened);
205
+
206
+ fs::remove_file(&legacy).unwrap();
207
+ let mut restarted = start(&data);
208
+ await_readiness(&mut restarted);
209
+ restarted.kill().unwrap();
210
+ restarted.wait().unwrap();
211
+ fs::remove_dir_all(directory).unwrap();
212
+ assert_missing_canonical_ownership_fails_closed();
213
+ }
214
+
215
+ fn assert_missing_canonical_ownership_fails_closed() {
216
+ let directory = fixture_dir();
217
+ fs::create_dir_all(&directory).unwrap();
218
+ let data = directory.join("feltdb.log");
219
+ fs::write(
220
+ data.with_extension("principals.json"),
221
+ serde_json::to_vec(&json!({
222
+ "principals": [{
223
+ "id": "agent_unresolved",
224
+ "application_id": PRODUCTION_SHAPED_APPLICATION,
225
+ "kind": "agent",
226
+ "name": "Unresolved Agent",
227
+ "version": 1,
228
+ "status": "active",
229
+ "created_by": "principal_delegator",
230
+ "created_at": 100,
231
+ "capabilities": [],
232
+ "parent_id": null,
233
+ "worker_id": null,
234
+ "started_at": null,
235
+ "completed_at": null
236
+ }],
237
+ "audit": []
238
+ }))
239
+ .unwrap(),
240
+ )
241
+ .unwrap();
242
+ let child = start(&data);
243
+ let output = await_failure(child);
244
+ assert!(output
245
+ .contains("PRINCIPAL_TENANT_MIGRATION_CONFLICT:MISSING_OWNERSHIP:app_pKEN4gJ0lvQV4D2ZWa"));
246
+ fs::remove_dir_all(directory).unwrap();
247
+ }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "create-feltdb",
3
3
  "private": false,
4
4
  "description": "Create a new FeltDB application with one command",
5
- "version": "0.9.0",
5
+ "version": "0.10.0",
6
6
  "license": "MIT",
7
7
  "bin": {
8
8
  "create-feltdb": "bin/create-feltdb.js"