Skip to main content

cratestack/
lib.rs

1//! CrateStack server facade — Postgres (sqlx) + Axum.
2//!
3//! This crate is the server-side slice of the framework. It re-exports the
4//! shared schema / parser / policy / SQL surface plus the sqlx (Postgres)
5//! runtime, Axum HTTP bindings, and the generated Rust client runtime.
6//!
7//! It deliberately does **not** depend on `cratestack-rusqlite`. That keeps
8//! `libsqlite3-sys` out of the dep graph, so consumers can use the official
9//! `sqlx` umbrella crate (which optionally declares `sqlx-sqlite` and trips
10//! Cargo's `links = "sqlite3"` collision rule) without needing a local
11//! `sqlx-shim` workaround.
12//!
13//! For embedded / mobile / wasm targets, depend on `cratestack-sqlite`
14//! instead. The two crates are strictly disjoint by design.
15//!
16//! Schema macros emit `::cratestack::*` paths, so consumers rename this
17//! crate via Cargo's `package =` field:
18//!
19//! ```toml
20//! [dependencies]
21//! cratestack = { package = "cratestack-pg", version = "0.4" }
22//! ```
23//!
24//! `sqlx`/`cratestack-sqlx` sit behind the default-on `postgres` Cargo
25//! feature (cratestack#329). A `db = None`-only consumer
26//! (`include_server_schema!(schema, db = None)`, cratestack#328) can drop
27//! `sqlx` from its dependency graph entirely:
28//!
29//! ```toml
30//! [dependencies]
31//! cratestack = { package = "cratestack-pg", version = "0.4", default-features = false }
32//! ```
33//!
34//! See `docs/design/no-database-mode.md` for when `db = None` applies and
35//! what it gives up.
36
37// Both `cratestack_core` and `cratestack_axum` expose `codec` and
38// `transport` modules, and the facade re-exports both crates with a glob.
39// The overlap is intentional — consumers reach those via the originating
40// crate's path, not the facade root — so silence the ambiguity warning
41// rather than dropping either glob.
42#![allow(ambiguous_glob_reexports)]
43
44// Re-exported so the axum dispatch tokens `cratestack-macros` generates
45// for `@stream` procedures (`crate::axum::procedure::invoke_call`) can
46// reference `::cratestack::async_stream::stream!` without every
47// consumer adding `async-stream` to their own `Cargo.toml`. Needed
48// because the `ProcedureRegistry` trait method's `db`/`ctx` parameters
49// are borrowed (`&Cratestack`/`&CratestackContext`), and — per return-position
50// `impl Trait` in traits' default lifetime-capture rules — the returned
51// `Stream` is only valid as long as those borrows are; wrapping the
52// call in a self-contained `async_stream::stream!` generator that owns
53// `db`/`ctx`/`registry`/`args` internally is what lets the resulting
54// `Stream` outlive the dispatch function's own stack frame (needed
55// since it travels all the way into the HTTP response body). Mirrors
56// how `futures` is re-exported below for the same
57// "codegen references a fixed path, consumers shouldn't have to
58// duplicate the dependency" reason.
59pub use async_stream;
60pub use chrono;
61pub use cratestack_client_rust as client_rust;
62// The wire codecs, at the facade root so a consumer never adds
63// `cratestack-codec-cbor` / `cratestack-codec-json` just to name one
64// (`envelope_layer`/`rpc_router` callers and `CratestackClient::new` take a
65// codec). They are the same types as `client_rust::CborCodec` /
66// `client_rust::JsonCodec`; this only makes the path discoverable.
67pub use cratestack_client_rust::CborCodec;
68#[cfg(feature = "codec-json")]
69pub use cratestack_client_rust::JsonCodec;
70pub use cratestack_core::*;
71// Re-exported (renamed from the `futures-util` crate, which is what
72// actually implements it) so `@stream` procedures' generated
73// `ProcedureRegistry` trait method — `impl ::cratestack::futures::Stream<
74// Item = Result<T, CratestackError>> + Send` (see
75// `cratestack-macros/src/procedure.rs`) — has somewhere to point without
76// every consumer adding its own `futures`/`futures-core`/`futures-util`
77// dependency. Mirrors how `chrono`/`uuid` are re-exported above for the
78// same "codegen references a fixed path, consumers shouldn't have to
79// duplicate the dependency" reason.
80pub use cratestack_macros::{
81    include_client_schema, include_embedded_schema, include_server_schema,
82};
83pub use cratestack_parser::{SchemaError, parse_schema, parse_schema_file, parse_schema_named};
84pub use cratestack_policy::{
85    PolicyExpr, PolicyLiteral, ProcedureArgs, ProcedurePolicy, ProcedurePolicyExpr,
86    ProcedurePolicyLiteral, ProcedurePredicate, ReadPolicy, ReadPredicate, RelationQuantifier,
87    authorize_procedure, authorize_query,
88};
89pub use futures_util as futures;
90
91// SQL primitives shared by every backend — re-exported directly from
92// `cratestack-sql` so consumers don't transit through `cratestack-sqlx`.
93pub use cratestack_sql::{
94    CoalesceExpr, CoalesceFilter, ConflictTarget, CreateDefault, CreateDefaultType,
95    CreateModelInput, FieldRef, Filter, FilterExpr, FilterOp, GeneratedDefault, GeneratedValue,
96    IntoColumnName, IntoSqlValue, JsonFilter, JsonTextPath, KeysetColumn, KeysetLeaf, ModelColumn,
97    ModelDescriptor, ModelPrimaryKey, NullOrder, OrderCatalog, OrderClause, OrderRelationEdge,
98    OrderTarget, Orderable, Projection, ReadSource, RelatedReadScope, RelationFilter, RelationHop,
99    RelationInclude, ResolvedOrderTarget, SortDirection, SqlColumnValue, SqlValue, Unorderable,
100    UpdateModelInput, UpsertModelInput, VectorDistanceExpr, VectorDistanceFilter, VectorMetric,
101    ViewDescriptor, WriteSource, coalesce, is_orderable, keyset_after, keyset_order_spec,
102    order_value_sql, parse_keyset_sort, resolve_order_target, wrap_filter,
103};
104/// PostGIS query surface (cratestack#842) — gated in
105/// `cratestack-sql` and forwarded through this crate's own
106/// `postgis` feature.
107#[cfg(feature = "postgis")]
108pub use cratestack_sql::{SpatialDistanceExpr, SpatialFilter, SpatialPoint, point};
109
110pub use regex;
111pub use serde;
112pub use serde_json;
113pub use tracing;
114pub use uuid;
115
116// `Json<T>` resolves to `cratestack_sqlx::Json<T>` on the server so
117// `sqlx::FromRow` decodes Postgres `jsonb` columns into it directly, using
118// the plain/untagged codec (cratestack#162) rather than going through
119// `serde_json` generically. (For `T = Value`, `Value`'s own hand-written
120// `Serialize`/`Deserialize` is untagged too, since cratestack#506 — the
121// two codecs agree on shape, they're just independently maintained.) This is
122// only possible with the `postgres` feature enabled (cratestack#329): models
123// (the only place a "Json" column is decoded from a row) can never exist
124// under `db = None` (cratestack#327's guard), so a `postgres`-disabled build
125// falls back to `cratestack-core`'s own backend-agnostic `Json<T>` newtype,
126// which is all a `db = None` schema's procedure args/returns ever need —
127// they only flow through serde (JSON/CBOR codecs), never `sqlx::FromRow`.
128#[cfg(not(feature = "postgres"))]
129pub use cratestack_core::json::Json;
130#[cfg(feature = "postgres")]
131pub use cratestack_sqlx::Json;
132
133// `Vector(n)` model fields decode/encode through `pgvector::Vector` at
134// the sqlx boundary (see `cratestack-macros`' generated `FromRow` impl
135// and `SqlValue::Vector` bind path) — re-exported so macro-emitted
136// `::cratestack::pgvector::Vector` paths resolve. Requires this
137// facade's own `pgvector` feature, which forwards to both
138// `cratestack-macros/pgvector` (the compile-time declaration gate)
139// and `cratestack-sqlx/pgvector` (the real column codec) in lockstep.
140#[cfg(feature = "pgvector")]
141pub use cratestack_sqlx::pgvector;
142
143// `Geography`/`Geometry` model fields decode through
144// `cratestack_sqlx::Ewkb` at the sqlx row boundary, so the generated
145// `::cratestack::Ewkb` path has to resolve here (cratestack#842).
146// Requires this facade's own `postgis` feature, which forwards to both
147// `cratestack-macros/postgis` (the compile-time declaration gate) and
148// `cratestack-sqlx/postgis` (the column codec) in lockstep.
149#[cfg(feature = "postgis")]
150pub use cratestack_sqlx::Ewkb;
151
152// -----------------------------------------------------------------------------
153// Server surface — axum, audit/idempotency/migrations/isolation.
154// -----------------------------------------------------------------------------
155
156pub use cratestack_axum::axum;
157pub use cratestack_axum::*;
158
159// Disambiguate the `rpc` module path. Both `cratestack_core` (wire shapes)
160// and `cratestack_axum` (binding helpers) expose an `rpc` module, so the
161// two `pub use ..::*` globs collide on the name and `::cratestack::rpc::*`
162// resolves non-deterministically. Macro-emitted code in `transport rpc`
163// schemas references symbols like `encode_rpc_error`,
164// `convert_handler_error_response`, `response_to_frame`, and
165// `RPC_BINDING_CAPABILITIES` — all of which live in `cratestack-axum::rpc`.
166// An explicit `pub use` re-export takes precedence over the globs, pinning
167// `::cratestack::rpc` to the axum module (which itself re-exports the wire
168// types from `cratestack-core::rpc`).
169pub use cratestack_axum::rpc;
170
171/// The MCP binding (ADR 0002, cratestack#1038), behind the `mcp` feature:
172/// `StdioServer` and `StreamableHttpServer` (cratestack#1039), the `McpTools`
173/// trait the generated `cratestack_schema::mcp` module implements, and the
174/// runtime helpers it names.
175#[cfg(feature = "mcp")]
176pub use cratestack_mcp as mcp;
177
178/// The WebTransport subscription binding
179/// (`docs/design/subscriptions-websocket.md` §6a). A schema declaring
180/// `extension webtransport { }` binds it to its generated dispatcher with
181/// `db.subscriptions().webtransport(config, auth)`.
182#[cfg(feature = "webtransport")]
183pub use cratestack_webtransport as webtransport;
184
185/// The WebSocket subscription binding
186/// (`docs/design/subscriptions-websocket.md` §6). A schema declaring
187/// `extension websocket { }` mounts it with
188/// `router.merge(db.subscriptions().websocket(config, auth))`.
189#[cfg(feature = "websocket")]
190pub use cratestack_websocket as websocket;
191
192// Everything below is sqlx (Postgres)-backed and only compiled in when the
193// default-on `postgres` feature is enabled (cratestack#329). A `db = None`
194// -only consumer builds with `default-features = false` (or explicitly
195// disables `postgres`) to drop `sqlx`/`cratestack-sqlx` from its dependency
196// graph entirely — nothing generated under `db = None` ever references these
197// symbols, since models (the only consumers of them) can never exist in a
198// `datasource { provider = "none" }` schema.
199/// Without `postgres` (`db = None`) the backplane-only handle, which
200/// `cratestack-api` exports too, rather than the glob's schema-AST `Topic`.
201#[cfg(not(feature = "postgres"))]
202pub use cratestack_core::TopicHandle as Topic;
203#[cfg(feature = "postgres")]
204pub use cratestack_sqlx::AUDIT_TABLE_DDL;
205/// Subscription backplanes (`docs/design/subscriptions-websocket.md` §4):
206/// `PgNotifyBackplane` is the default; `InProcessBackplane` serves a single
207/// replica. Swap with `Cratestack::builder(pool).with_backplane(..)`.
208#[cfg(feature = "postgres")]
209pub use cratestack_sqlx::backplane::{InProcessBackplane, PgNotifyBackplane};
210#[cfg(feature = "postgres")]
211pub use cratestack_sqlx::sqlx;
212/// A schema `topic`'s handle (slice S4): `db.topics().<topic>(key)`.
213#[cfg(feature = "postgres")]
214pub use cratestack_sqlx::topic::Topic;
215#[cfg(feature = "postgres")]
216pub use cratestack_sqlx::{
217    Aggregate, AggregateColumn, AggregateCount, CreateRecord, DeleteMany, DeleteRecord, FindMany,
218    FindManyWith, FindUnique, FromPartialPgRow, ModelDelegate, ProjectedFindMany,
219    ProjectedFindUnique, RunInTxOutcome, ScopedAggregate, ScopedAggregateColumn,
220    ScopedAggregateCount, ScopedCreateRecord, ScopedDeleteMany, ScopedDeleteRecord, ScopedFindMany,
221    ScopedFindManyWith, ScopedFindUnique, ScopedModelDelegate, ScopedProjectedFindMany,
222    ScopedProjectedFindUnique, ScopedUpdateMany, ScopedUpdateManySet, ScopedUpdateRecord,
223    ScopedUpdateRecordSet, ScopedUpsertRecord, ScopedUpsertRecordDoNothing, SqlxIdempotencyStore,
224    UpdateMany, UpdateManySet, UpdateRecord, UpdateRecordSet, UpsertOutcome, UpsertRecord,
225    UpsertRecordDoNothing, ViewDelegate, ViewDelegateNoUnique, create_record_with_executor,
226    update_record_with_executor,
227};
228#[cfg(feature = "postgres")]
229pub use cratestack_sqlx::{
230    MIGRATIONS_TABLE_DDL, Migration, MigrationState, MigrationStatus, apply_pending,
231    ensure_migrations_table, status,
232};
233#[cfg(feature = "postgres")]
234pub use cratestack_sqlx::{
235    Tx, cratestack_error_from_sqlx, run_in_isolated_tx, run_in_isolated_tx_with_retries,
236};
237
238/// Crypto provider selection for FIPS-validated deployments.
239///
240/// **`crypto-aws-lc-rs` is not implemented yet.** Enabling it is a hard
241/// `compile_error!`, not a working FIPS mode — this used to return `Ok(())`
242/// without installing any provider, which is a false assurance in a
243/// compliance-facing API: a service that called this and checked for `Ok`
244/// got an affirmative return while still running on the non-FIPS `ring`
245/// backend. See <https://github.com/cratestack/cratestack/issues/334>.
246///
247/// Making this real requires the TLS backend becoming a genuine choice
248/// across `cratestack-sqlx` and `cratestack-client-rust` (both currently
249/// hard-select `ring`), not just adding `aws-lc-rs` as a dependency here —
250/// Cargo features are additive, so enabling `crypto-aws-lc-rs` today would
251/// only add a second provider alongside `ring`, not replace it. Until that
252/// backend-selection work lands, this function fails to compile under the
253/// feature rather than silently lying about what it installed.
254pub fn install_fips_crypto_provider() -> Result<(), cratestack_core::CratestackError> {
255    #[cfg(feature = "crypto-aws-lc-rs")]
256    {
257        compile_error!(
258            "cratestack-pg's `crypto-aws-lc-rs` feature does not install a FIPS-validated \
259             crypto provider yet — see install_fips_crypto_provider's doc comment and \
260             https://github.com/cratestack/cratestack/issues/334. Do not enable this feature."
261        )
262    }
263    #[cfg(not(feature = "crypto-aws-lc-rs"))]
264    {
265        Err(cratestack_core::CratestackError::Internal(
266            "cratestack was not compiled with `crypto-aws-lc-rs` feature; \
267             FIPS-validated crypto provider is unavailable"
268                .to_owned(),
269        ))
270    }
271}
272
273mod envelope_fn;
274
275// Doctest-only: the compile-time half of the `@isolation` escape test.
276#[cfg(all(doctest, feature = "postgres"))]
277mod isolation_doctests;
278
279#[doc(hidden)]
280pub mod __private {
281    /// The generated `envelope_layer`'s body, which this facade's
282    /// `__envelope_layer_fn!` forwards to under its `envelope` feature
283    /// (`envelope_fn.rs`).
284    #[cfg(feature = "envelope")]
285    pub use cratestack_axum::__envelope_layer_fn_body as envelope_layer_fn_body;
286
287    #[cfg(feature = "postgres")]
288    pub use cratestack_sqlx::SqlxRuntime;
289    // Not part of the public API surface — the generated
290    // `Cratestack::dispatch_audit_sink` (cratestack#534) is the
291    // consumer-facing wrapper around this; see its doc comment.
292    #[cfg(feature = "postgres")]
293    pub use cratestack_sqlx::dispatch_audit_sink;
294
295    /// Re-exports for the macro-emitted RPC dispatcher. Not part of the
296    /// public API surface — schema authors should never reference these
297    /// directly. Public helpers live at `cratestack::rpc::*`.
298    pub use cratestack_axum::rpc::{decode_rpc_body, encode_rpc_value, response_to_frame};
299
300    /// `@@subscribe` SSE dispatch (`GET /rpc/subscribe/{op_id}`, design
301    /// doc §3.4a, cratestack#390): the bounded-channel bridge from a
302    /// `CratestackEventBus` push callback to a `Stream`, and the encoder that
303    /// turns that `Stream` into a `text/event-stream` response. Not
304    /// part of the public API surface for the same reason as the rest
305    /// of this module.
306    pub use cratestack_axum::rpc::{
307        FeedEvent, SubscribeQuery, encode_model_event_sse_response, encode_subscribe_error,
308        encode_subscription_sse_response, guarded_receiver_stream, model_events, model_feed,
309        project_event_fields, route_event, split_subscribe_query, subscribe_input_query,
310        subscription_channel, until_credential_expires, validate_subscribe_accept_header,
311    };
312    /// A `subscription procedure`'s arguments and items (slice S4).
313    pub use cratestack_axum::rpc::{procedure_feed, procedure_input_query, procedure_query_args};
314    /// A subscription feed's filter, decided in memory per event (slice S3).
315    pub use cratestack_sql::{filter_in_memory_columns, filter_matches};
316    /// The subscriber's read policy, decided per event in the same
317    /// dispatch (an event has no query to push the policy into).
318    #[cfg(feature = "postgres")]
319    pub use cratestack_sqlx::{
320        missing_policy_columns, record_readable, refuse_unreadable_subscription,
321    };
322}