New crates
- platform-nats: async_nats JetStream bridge; pull/push consumers, explicit ACK,
subject prefixing under provisioning.>, 6 stream definitions on startup
- platform-db: SurrealDB pool (embedded RocksDB solo, Surreal<Mem> tests,
WebSocket server multi-user); migrate() with DEFINE TABLE IF NOT EXISTS DDL
Service integrations
- orchestrator: NATS pub on task state transitions, execution_logs → SurrealDB,
webhook handler (HMAC-SHA256), AuditCollector (batch INSERT, 100-event/1s flush)
- control-center: solo_auth_middleware (intentional bypass, --mode solo only),
NATS session events, WebSocket bridge via JetStream subscription (no polling)
- vault-service: NATS lease flow; credentials over HTTPS only (lease_id in NATS);
SurrealDB storage backend with MVCC retry + exponential backoff
- secretumvault: complete SurrealDB backend replacing HashMap; 9 unit + 19 integration tests
- extension-registry: NATS lifecycle events, vault:// credential resolver with TTL cache,
cache invalidation via provisioning.workspace.*.deploy.done
Clippy workspace clean
cargo clippy --workspace -- -D warnings: 0 errors
Patterns fixed: derivable_impls (#[default] on enum variants), excessive_nesting
(let-else, boolean arithmetic in retain, extracted helpers), io_error_other,
redundant_closure, iter_kv_map, manual_range_contains, pathbuf_instead_of_path
55 lines
1.6 KiB
Rust
55 lines
1.6 KiB
Rust
use async_nats::jetstream::{
|
|
stream::{Config as StreamConfig, RetentionPolicy, StorageType},
|
|
Context,
|
|
};
|
|
use tracing::info;
|
|
|
|
use crate::Error;
|
|
|
|
/// Provision all six JetStream streams required by the provisioning platform.
|
|
/// Idempotent — uses `get_or_create_stream` so safe to call on every startup.
|
|
pub async fn ensure_provisioning_streams(js: &Context) -> Result<(), Error> {
|
|
let streams: &[(&str, &[&str], RetentionPolicy)] = &[
|
|
(
|
|
"TASKS",
|
|
&["provisioning.tasks.>"],
|
|
RetentionPolicy::WorkQueue,
|
|
),
|
|
(
|
|
"VAULT",
|
|
&["provisioning.vault.>"],
|
|
RetentionPolicy::Interest,
|
|
),
|
|
("AUTH", &["provisioning.auth.>"], RetentionPolicy::Interest),
|
|
(
|
|
"WORKSPACE",
|
|
&["provisioning.workspace.>"],
|
|
RetentionPolicy::Limits,
|
|
),
|
|
("AUDIT", &["provisioning.audit.>"], RetentionPolicy::Limits),
|
|
(
|
|
"HEALTH",
|
|
&["provisioning.health.>"],
|
|
RetentionPolicy::Interest,
|
|
),
|
|
];
|
|
|
|
for (name, subjects, retention) in streams {
|
|
let cfg = StreamConfig {
|
|
name: name.to_string(),
|
|
subjects: subjects.iter().map(|s| s.to_string()).collect(),
|
|
retention: *retention,
|
|
storage: StorageType::File,
|
|
..Default::default()
|
|
};
|
|
|
|
js.get_or_create_stream(cfg)
|
|
.await
|
|
.map_err(|e| Error::Stream(format!("failed to ensure stream {name}: {e}")))?;
|
|
|
|
info!(stream = name, "JetStream stream ready");
|
|
}
|
|
|
|
Ok(())
|
|
}
|