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/// Host-side parse-and-rewrite confinement of a guest's **raw-SQL target read** (R4/D8): the
52/// AST-level analog of the `orm` path's `PerTableTarget` per-table confinement, injecting
53/// `tenant = B AND <public subset>` onto EVERY table reference so a target read of another tenant
54/// `B` can reach only B's declared public rows — the guest cannot reposition or `OR`-escape it.
55pub mod target_sql;
56/// The one canonical wall-clock read for native crates (`now_unix`/`now_unix_ms`).
57pub mod time;
58
59// The shared wasm-clean layer lives in `boatramp-types`; re-export it so the
60// `boatramp_core::config`/`::route`/`::matcher`/`::domain_verify`/… paths are
61// unchanged. (`compute` is its own module above — it re-exports the types layer.)
62pub use boatramp_types::{
63 access, authz, blob_notify, config, cron, daemon_config, dns_managed, domain_verify, function,
64 gateway, geo, host, logs, matcher, predicate, route, security, site, tenancy, waf, workflow,
65};
66pub use boatramp_types::{schema_version, SCHEMA_VERSION};
67
68pub use error::{ConfigError, DeployError, KvError, StorageError};
69pub use mode::DeploymentMode;
70
71/// A streaming, owned sequence of byte chunks.
72///
73/// Each chunk is yielded as it becomes available; the full payload is never
74/// collected in memory.
75pub type ByteStream = BoxStream<'static, Result<Bytes, StorageError>>;
76
77/// Metadata describing a stored object.
78#[derive(Debug, Clone, Default, PartialEq, Eq)]
79pub struct ObjectMeta {
80 /// Storage key (path) of the object.
81 pub key: String,
82 /// Size in bytes, when known ahead of streaming.
83 pub size: Option<u64>,
84 /// MIME content type, when known.
85 pub content_type: Option<String>,
86 /// Backend-specific entity tag, when available.
87 pub etag: Option<String>,
88}
89
90/// Metadata supplied when writing an object.
91#[derive(Debug, Clone, Default)]
92pub struct PutMeta {
93 /// MIME content type to record for the object.
94 pub content_type: Option<String>,
95}
96
97/// The result of a streaming read: object metadata plus its byte stream.
98pub struct GetObject {
99 /// Metadata for the object being read.
100 pub meta: ObjectMeta,
101 /// The object's body, streamed chunk by chunk.
102 pub body: ByteStream,
103}
104
105/// How an object under a watched prefix changed (FA-5 blob-change triggers).
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
107pub enum BlobChangeKind {
108 /// An object was created.
109 Created,
110 /// An existing object's bytes changed.
111 Modified,
112 /// An object was removed.
113 Removed,
114}
115
116/// A single change event under a watched prefix — a backend-native notification
117/// ([`Storage::watch`]), never boatramp's own write path (so the semantics are the
118/// same whoever wrote it).
119#[derive(Debug, Clone, PartialEq, Eq)]
120pub struct BlobChange {
121 /// The full storage key that changed.
122 pub key: String,
123 /// What happened to it.
124 pub kind: BlobChangeKind,
125}
126
127/// A stream of change events under a watched prefix, live until dropped.
128pub type ChangeStream = BoxStream<'static, BlobChange>;
129
130/// A pluggable, streaming object-storage backend.
131///
132/// Implementations MUST stream data without buffering whole objects in memory.
133#[async_trait::async_trait]
134pub trait Storage: Send + Sync {
135 /// Open an object for streaming reads.
136 async fn get(&self, key: &str) -> Result<GetObject, StorageError>;
137
138 /// Open a byte range for streaming reads (for HTTP `Range`). `len == None`
139 /// means "from `offset` to the end".
140 async fn get_range(
141 &self,
142 key: &str,
143 offset: u64,
144 len: Option<u64>,
145 ) -> Result<GetObject, StorageError>;
146
147 /// Stream `body` into the backend at `key`, returning the stored metadata.
148 async fn put(
149 &self,
150 key: &str,
151 body: ByteStream,
152 meta: PutMeta,
153 ) -> Result<ObjectMeta, StorageError>;
154
155 /// Fetch object metadata without reading its body.
156 async fn head(&self, key: &str) -> Result<ObjectMeta, StorageError>;
157
158 /// Delete an object. Deleting a missing object is not an error.
159 async fn delete(&self, key: &str) -> Result<(), StorageError>;
160
161 /// List object metadata under `prefix`.
162 async fn list(&self, prefix: &str) -> Result<Vec<ObjectMeta>, StorageError>;
163
164 /// If this backend stores objects as local files, memory-map `key` and return
165 /// its bytes for zero-copy serving. The blob keyspace is content-addressed and
166 /// immutable (a file is never modified after it is written), so a mapping can
167 /// never see a truncated/rewritten file — the one hazard that makes `mmap`
168 /// unsafe. Returns `None` for remote/opaque backends (S3/GCS/Azure) or on any
169 /// error, so the caller falls back to streaming. Large static bodies use this
170 /// to skip `tokio::fs`'s internal double-buffering copy (and serve one
171 /// content-length body instead of a chunked stream).
172 fn mapped(&self, _key: &str) -> Option<bytes::Bytes> {
173 None
174 }
175
176 /// If this backend stores objects as local files, open `key` and return the
177 /// file handle for the zero-copy `sendfile` serving path — the kernel moves the
178 /// file's bytes straight to the client socket with no userspace copy (what
179 /// nginx/caddy do for plaintext static). Same content-addressed-immutability
180 /// guarantee as [`mapped`](Storage::mapped). Returns `None` for remote/opaque
181 /// backends (S3/GCS/Azure) or on any error, so the caller falls back to
182 /// `mapped`/streaming. The caller decides whether `sendfile` is applicable
183 /// (plaintext only — TLS can't zero-copy through userspace crypto).
184 fn local_file(&self, _key: &str) -> Option<std::fs::File> {
185 None
186 }
187
188 /// Whether this backend can natively watch for changes (FA-5 blob-change
189 /// triggers). A cheap, side-effect-free capability probe: a `Blob` trigger is
190 /// **refused at activation** on a backend that returns `false`, so the
191 /// semantics never silently degrade. Defaults to `false`.
192 fn supports_watch(&self) -> bool {
193 false
194 }
195
196 /// Watch for changes under `prefix`, returning a live stream of
197 /// [`BlobChange`]s until dropped (backend-native notification — inotify /
198 /// FSEvents locally, SQS / Pub/Sub / Event Grid for cloud stores). `Ok(None)`
199 /// means this backend does not support watching (the default), matching
200 /// [`supports_watch`](Self::supports_watch).
201 async fn watch(&self, _prefix: &str) -> Result<Option<ChangeStream>, StorageError> {
202 Ok(None)
203 }
204}