boatramp_core/lib.rs
1//! Core domain types for boatramp.
2//!
3//! - [`Storage`] — the streaming-first blob backend trait (filesystem, S3, ...).
4//! No method buffers a whole object in memory.
5//! - [`kv`] — a tiny pluggable [`kv::KvStore`] for small deploy metadata, with
6//! an LRU [`kv::CachedKv`] wrapper.
7//! - [`deploy`] — content-addressed, atomically-activated deployments built on
8//! top of a [`Storage`] (blobs) plus a [`kv::KvStore`] (manifests + pointers).
9//! - [`config`] — deploy-scoped configuration (the `routing` section of
10//! `project.cfg`), folded into the manifest; [`matcher`] — the shared
11//! path-pattern engine it relies on.
12
13use bytes::Bytes;
14use futures::stream::BoxStream;
15
16pub mod blob_provision;
17pub mod cache_coherence;
18#[cfg(feature = "authz")]
19pub mod cedar;
20pub mod cert;
21pub mod compat;
22#[cfg(feature = "authz")]
23pub mod cose;
24pub mod envelope;
25// `compute` extends the wasm-clean `boatramp_types::compute` (re-exported within)
26// with the native control-plane layer: the `ComputeBackend` trait, the scheduler,
27// and the reconcile logic.
28pub mod compute;
29pub mod deploy;
30/// Per-project SMTP email-profile store (sealed password) backing the `email`
31/// guest capability — credentials host-held, config admin-reconfigurable.
32pub mod email_config;
33pub mod error;
34/// Per-node guest-IP pool shared by the VMM (tap) + container (veth) backends.
35pub mod ipam;
36/// Posture-scaled kernel-trust verification (needs the `authz` signing primitives).
37#[cfg(feature = "authz")]
38pub mod kernel_trust;
39pub mod kv;
40pub mod messaging;
41/// Online, resumable migration of a pre-0.2.0 store to the project-scoped layout.
42pub mod migrate;
43pub mod mode;
44/// A typed query AST + injection-safe `?N` SQL compiler backing the `orm` handler binding.
45pub mod orm;
46pub mod project;
47pub mod secret_store;
48/// The duplex/resumable session delivery-semantics model (Stage 1 of `PLAN-session-primitive`).
49pub mod session;
50pub mod sql;
51/// The one canonical wall-clock read for native crates (`now_unix`/`now_unix_ms`).
52pub mod time;
53
54// The shared wasm-clean layer lives in `boatramp-types`; re-export it so the
55// `boatramp_core::config`/`::route`/`::matcher`/`::domain_verify`/… paths are
56// unchanged. (`compute` is its own module above — it re-exports the types layer.)
57pub use boatramp_types::{
58 access, authz, blob_notify, config, cron, daemon_config, dns_managed, domain_verify, function,
59 gateway, geo, host, logs, matcher, predicate, route, security, site, tenancy, waf, workflow,
60};
61pub use boatramp_types::{schema_version, SCHEMA_VERSION};
62
63pub use error::{ConfigError, DeployError, KvError, StorageError};
64pub use mode::DeploymentMode;
65
66/// A streaming, owned sequence of byte chunks.
67///
68/// Each chunk is yielded as it becomes available; the full payload is never
69/// collected in memory.
70pub type ByteStream = BoxStream<'static, Result<Bytes, StorageError>>;
71
72/// Metadata describing a stored object.
73#[derive(Debug, Clone, Default, PartialEq, Eq)]
74pub struct ObjectMeta {
75 /// Storage key (path) of the object.
76 pub key: String,
77 /// Size in bytes, when known ahead of streaming.
78 pub size: Option<u64>,
79 /// MIME content type, when known.
80 pub content_type: Option<String>,
81 /// Backend-specific entity tag, when available.
82 pub etag: Option<String>,
83}
84
85/// Metadata supplied when writing an object.
86#[derive(Debug, Clone, Default)]
87pub struct PutMeta {
88 /// MIME content type to record for the object.
89 pub content_type: Option<String>,
90}
91
92/// The result of a streaming read: object metadata plus its byte stream.
93pub struct GetObject {
94 /// Metadata for the object being read.
95 pub meta: ObjectMeta,
96 /// The object's body, streamed chunk by chunk.
97 pub body: ByteStream,
98}
99
100/// How an object under a watched prefix changed (FA-5 blob-change triggers).
101#[derive(Debug, Clone, Copy, PartialEq, Eq)]
102pub enum BlobChangeKind {
103 /// An object was created.
104 Created,
105 /// An existing object's bytes changed.
106 Modified,
107 /// An object was removed.
108 Removed,
109}
110
111/// A single change event under a watched prefix — a backend-native notification
112/// ([`Storage::watch`]), never boatramp's own write path (so the semantics are the
113/// same whoever wrote it).
114#[derive(Debug, Clone, PartialEq, Eq)]
115pub struct BlobChange {
116 /// The full storage key that changed.
117 pub key: String,
118 /// What happened to it.
119 pub kind: BlobChangeKind,
120}
121
122/// A stream of change events under a watched prefix, live until dropped.
123pub type ChangeStream = BoxStream<'static, BlobChange>;
124
125/// A pluggable, streaming object-storage backend.
126///
127/// Implementations MUST stream data without buffering whole objects in memory.
128#[async_trait::async_trait]
129pub trait Storage: Send + Sync {
130 /// Open an object for streaming reads.
131 async fn get(&self, key: &str) -> Result<GetObject, StorageError>;
132
133 /// Open a byte range for streaming reads (for HTTP `Range`). `len == None`
134 /// means "from `offset` to the end".
135 async fn get_range(
136 &self,
137 key: &str,
138 offset: u64,
139 len: Option<u64>,
140 ) -> Result<GetObject, StorageError>;
141
142 /// Stream `body` into the backend at `key`, returning the stored metadata.
143 async fn put(
144 &self,
145 key: &str,
146 body: ByteStream,
147 meta: PutMeta,
148 ) -> Result<ObjectMeta, StorageError>;
149
150 /// Fetch object metadata without reading its body.
151 async fn head(&self, key: &str) -> Result<ObjectMeta, StorageError>;
152
153 /// Delete an object. Deleting a missing object is not an error.
154 async fn delete(&self, key: &str) -> Result<(), StorageError>;
155
156 /// List object metadata under `prefix`.
157 async fn list(&self, prefix: &str) -> Result<Vec<ObjectMeta>, StorageError>;
158
159 /// If this backend stores objects as local files, memory-map `key` and return
160 /// its bytes for zero-copy serving. The blob keyspace is content-addressed and
161 /// immutable (a file is never modified after it is written), so a mapping can
162 /// never see a truncated/rewritten file — the one hazard that makes `mmap`
163 /// unsafe. Returns `None` for remote/opaque backends (S3/GCS/Azure) or on any
164 /// error, so the caller falls back to streaming. Large static bodies use this
165 /// to skip `tokio::fs`'s internal double-buffering copy (and serve one
166 /// content-length body instead of a chunked stream).
167 fn mapped(&self, _key: &str) -> Option<bytes::Bytes> {
168 None
169 }
170
171 /// If this backend stores objects as local files, open `key` and return the
172 /// file handle for the zero-copy `sendfile` serving path — the kernel moves the
173 /// file's bytes straight to the client socket with no userspace copy (what
174 /// nginx/caddy do for plaintext static). Same content-addressed-immutability
175 /// guarantee as [`mapped`](Storage::mapped). Returns `None` for remote/opaque
176 /// backends (S3/GCS/Azure) or on any error, so the caller falls back to
177 /// `mapped`/streaming. The caller decides whether `sendfile` is applicable
178 /// (plaintext only — TLS can't zero-copy through userspace crypto).
179 fn local_file(&self, _key: &str) -> Option<std::fs::File> {
180 None
181 }
182
183 /// Whether this backend can natively watch for changes (FA-5 blob-change
184 /// triggers). A cheap, side-effect-free capability probe: a `Blob` trigger is
185 /// **refused at activation** on a backend that returns `false`, so the
186 /// semantics never silently degrade. Defaults to `false`.
187 fn supports_watch(&self) -> bool {
188 false
189 }
190
191 /// Watch for changes under `prefix`, returning a live stream of
192 /// [`BlobChange`]s until dropped (backend-native notification — inotify /
193 /// FSEvents locally, SQS / Pub/Sub / Event Grid for cloud stores). `Ok(None)`
194 /// means this backend does not support watching (the default), matching
195 /// [`supports_watch`](Self::supports_watch).
196 async fn watch(&self, _prefix: &str) -> Result<Option<ChangeStream>, StorageError> {
197 Ok(None)
198 }
199}