Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/monetize

crates/monetize-server/Cargo.toml

[package]
name = "monetize-server"
description = "the monetize service: gRPC control plane, vendor webhooks, the usage poller, the license authority — a skidbladnir-managed fat atom"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
repository.workspace = true
license.workspace = true
authors.workspace = true
publish.workspace = true

[[bin]]
name = "monetize-server"
path = "src/main.rs"

[features]
# The custody of private estates' secrets (`custody.rs`) with a real seal.
custody-ring = ["dep:ring"]
# **The test doubles** (`testing`: CallbackPay, TestServer, the test lifecycles, products and
# guardian; A28 M15): never in `default`, so no shipped binary carries them. Tests turn it on
# (this crate's own through the dev-dependency on itself).
testing = []
# **The money-free open default** (owner 2026-10-24): a fresh open build sells free plans with no
# card check and lays on play iron, through the open dummies — the `sandbox` vendor and cloud and
# the `invoice` vendor — until a payment guest and a cloud guest are fed. They were kept out of
# `default` for a production build's sake; the ONE box's production money is a SIGNED GUEST it is
# fed, never a linked dummy, and the dummies refuse to pretend (every sandbox fact says so).
# `custody-ring` too: the open default sells private estates (a Lake on the sandbox cloud), and a
# sold estate's secrets are sealed or not kept at all.
default = ["invoice", "sandbox", "custody-ring"]
# Vendors and clouds are cargo features so a deployment links only what it has a
# contract with (the monetize-pay / monetize-cloud rule, forwarded). THE SPLIT
# (2026-09-05): only the OPEN impls are features here. The paid ones — klarna,
# stripe, swish, upcloud, scaleway — are the vetra build's, a separate binary in
# the private vetra-automation/monetize-impl that calls `cli::run` with their
# factories registered on top of `Linked::open()`. This crate never names them.
invoice = ["monetize-pay/invoice"]
# The REHEARSAL build: the vendor with infinite funds and no socket, and the
# cloud with fake iron at declared prices. In `default` since monetize v2.0 P1 (the
# money-free open default, owner 2026-10-24); a build may still turn it off. See `monetize_pay::sandbox` and
# `monetize_cloud::sandbox`. The vetra build's `sandbox` prices off UpCloud's
# real list (the only network call it can make).
sandbox = ["monetize-pay/sandbox", "monetize-cloud/sandbox"]
# Turn every `functional_status` row this server emits into a real nornir
# testmatrix row (skidbladnir's feature, forwarded). Off: a compiled-out no-op.
testmatrix = ["skidbladnir/testmatrix"]
# **The DEV-NARRATE receiver** (lane N2): the ONE box (monetize-admin) listens to a narrated test
# site's sealed boot logs (`estate_order::narrate`). Off by default: a monetize that lays no
# narrated site links no crypto_box.
narrate = ["dep:nordisk-narrate", "dep:crypto_box"]

[dependencies]
monetize = { path = "../monetize" }
monetize-pay = { path = "../monetize-pay" }
monetize-cloud = { path = "../monetize-cloud" }
monetize-product = { path = "../monetize-product" }
# The ONE estate renderer (2026-10-04), shared with gunnar's console: this
# server keeps only the config's half and the plugin's half (`estate_svg`).
nordisk-estate-svg = { path = "../estate-svg", version = "0.1.0" }
# The guest host: `[products.<id>] guest = "<path>.wasm"` builds a product from a
# signed module, behind the same factory seam a linked plugin uses. The ONE
# product-shaped thing the open server links, and it is not a product.
monetize-product-wasm = { path = "../monetize-product-wasm" }
# The ONE mail path (g-nonpay, g-renewal): a customer notice the sink cannot
# send goes through the host's relay (`signup::HotSink::notify`).
monetize-mail = { path = "../monetize-mail" }
# The CLOUD guest host: `[clouds.<name>] guest = "<name>.wasm"` is a cloud the
# server takes in live and wraps in its spend ceiling like any linked one.
monetize-cloud-wasm = { path = "../monetize-cloud-wasm" }
monetize-embed = { path = "../monetize-embed" }
monetize-guardian = { path = "../monetize-guardian" }
monetize-proto = { path = "../monetize-proto" }

tonic.workspace = true
prost.workspace = true
# tonic's `router` feature already links axum 0.8 (and nornir-service requires that
# feature), so the vendor webhook rides on the same axum router as the gRPC services:
# one listener, one port, no second HTTP stack.
axum.workspace = true
tokio = { workspace = true, features = ["rt-multi-thread", "macros", "sync", "time", "net", "signal"] }
# `BroadcastStream`, which is what turns the event bus's bounded ring into a
# tonic server stream — including the Lagged signal the gap marker is built from.
tokio-stream = { version = "0.1", features = ["sync"] }
serde.workspace = true
serde_json.workspace = true
sha2 = "0.11"
anyhow.workspace = true
thiserror.workspace = true
redb.workspace = true
toml = "1"
# The plugin update (`plugins`, 2026-10-17): the module's own signature checked by the very
# verifier the loader uses; which guest the bytes are read by the guest host's probe.
guest-host = { path = "../../../edda/crates/guest-host", features = ["wasmtime", "verify-modules"] }
monetize-product-guest = { path = "../monetize-product-guest" }
# The plugin update runs as a QUEUED job on a jera chain lane (owner rule: background work is
# a jera job, a UI ask never blocks), its records kept in memory for `Plugins.Status`.
jera = { version = "0.2.1", default-features = false }
# The release bucket, read over HTTPS (the same client and roots monetize-admin's self update uses).
reqwest = { version = "0.13", default-features = false, features = ["rustls-no-provider", "blocking"] }
rustls = { version = "0.23", default-features = false, features = ["ring", "std"] }
webpki-root-certs = "1"

# Every service in the suite goes through skidbladnir — unit generation,
# install/uninstall, the system user, functional_status rows.
skidbladnir.workspace = true
# The ONE bearer-auth interceptor + token ladder the nordisk servers share.
nornir-service = { version = "0.1.9" }
constant_time_eq = "0.6"
# **The custody's seal** (`seal.rs`, private gunnars): AES-256-GCM and the
# system's randomness — ring, already in every graph this crate is in. OPTIONAL
# (`custody-ring`), and that is the point: a new edge on monetize-server would
# change the lock of every workspace that links it (gunnar's, the kit's), so
# the seal is switched on only by the build that sells private estates.
ring = { version = "0.17", optional = true }
# The receiver half of Skidbladnir's nordisk-narrate (no `dev-narrate`: no sender is linked) and the
# key type its state machine takes — the same crypto_box spelling nordisk-narrate pins.
nordisk-narrate = { version = "0.1.0", optional = true }
crypto_box = { version = "0.9", default-features = false, features = ["alloc", "getrandom", "salsa20"], optional = true }

[dev-dependencies]
# The host's own tests load the toy (a TEST-kind guest) under a `[products]` block: only under
# the test-only flag, never in a build that ships (RA2-12).
monetize-product-guest = { path = "../monetize-product-guest", features = ["test-guests"] }
# P3-FLEET (plan §8): the shared front door (`sold_door::ShopDoor`) over the real Fleet and
# SoldEstate services, both served by the ONE box for one release (estate_order/tests/fleet_front.rs).
monetize-estate-side = { path = "../monetize-estate-side", default-features = false, features = ["sold-door"] }
tokio-stream = { version = "0.1", features = ["sync", "net"] }
# This crate's own tests take its test doubles (feature `testing`, A28 M15).
monetize-server = { path = ".", features = ["testing"] }
# PAID-WORDS: the fake ONE box every site's tests stand on says what this box says while a
# paid account settles (`tests/pull_estate.rs`).
monetize-site-buy = { path = "../monetize-site-buy", features = ["testkit"] }
# The narrate receiver's test drives a local SENDER (lane N2).
nordisk-narrate = { version = "0.1.0", features = ["dev-narrate"] }
# A standard MIME parser reads the ready mail back (2026-10-14).
mail-parser = "0.11"
# The custody's tests seal for real (`seal.rs` under `cfg(test)`).
ring = "0.17"
# The open BINARY links no product (owner ruling 2026-09-22): a product reaches the ONE box
# as its signed guest. The tests drive the registry and the purchase path through the
# Gunnar- and Holger-shaped TEST products (`testing::products`; the native plugin crates
# went in v2.0 P2, A25 2.1/2.2).
# The shared box fixture (`PullingBox`), kept when the native product crates go (v2.0 P2).
monetize-embed = { path = "../monetize-embed", features = ["testing"] }
ed25519-dalek.workspace = true
# `tests/guests_on_the_fly.rs` only: the CA and signer a live replacement is
# signed with (and the stranger's it is refused under). Already in the tree —
# monetize-product-wasm depends on it with the same features.
guest-host = { path = "../../../edda/crates/guest-host", features = ["wasmtime", "verify-modules"] }