Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 52 additions & 3 deletions crates/busbar-contract/include/busbar_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ extern "C" {
#define BB_MECH_SLOT_CLOSE UINT32_C(8) /* `close`. */
#define BB_MECH_SLOT_READY UINT32_C(0xffffffff) /* `ready`: the door's optional tail op ([`super::super::door::Door::ready`]), NOT a table */
#define BB_MECH_LIFECYCLE_SLOTS UINT32_C(9) /* How many lifecycle slots [`OpsHead`] holds. */
#define BB_MECH_KIND_STORE "store" /* `abi/store/`. */
#define BB_MECH_KIND_SECRET "secret" /* `abi/secret/`. */
#define BB_MECH_KIND_AUTH "auth" /* `abi/auth/`. */
#define BB_MECH_KIND_HOOK "hook" /* `abi/hook/`. */
#define BB_MECH_KIND_EXPORT "export" /* `abi/export/`. */
#define BB_MECH_KIND_PLANE "plane" /* `abi/plane/`. */
#define BB_MECH_KIND_TRANSPORT "transport" /* `abi/transport/`. */
#define BB_MECH_MECHANISM_VERSION UINT32_C(2) /* The mechanism's version, stamped in every [`door::Door`]. v1.5.5 called it `TRANSPORT_VERSION` */
#define BB_MECH_DOOR_MAGIC UINT64_C(0x4c50524142535542) /* ASCII `"BUSBARPL"`, little-endian: a door's first eight bytes. Deliberately not the retired */
#define BB_MECH_DOOR_SYMBOL "busbar_plugin_door" /* The ONE symbol a plugin exports, NUL-terminated for `dlsym`. */
Expand Down Expand Up @@ -564,7 +571,8 @@ extern "C" {
#define BB_HSVC_OP_NEED_ADMIT UINT32_C(19) /* `need.admit`. */
#define BB_HSVC_OP_TRUST_VERIFY UINT32_C(20) /* `trust.verify`. */
#define BB_HSVC_OP_RECORDS_SECRET UINT32_C(21) /* `records.secret`. */
#define BB_HSVC_SERVICES UINT32_C(22) /* How many services [`HostSlots`] holds. */
#define BB_HSVC_OP_DISK_APPEND UINT32_C(22) /* `disk.append`. */
#define BB_HSVC_SERVICES UINT32_C(23) /* How many services [`HostSlots`] holds. */
#define BB_HSVC_SECRET_NOT_LIVE UINT64_C(0) /* `value` of [`op::RECORDS_SECRET`]: not live, or no such credential. */
#define BB_HSVC_SECRET_LIVE UINT64_C(1) /* `value` of [`op::RECORDS_SECRET`]: the credential is live. */
#define BB_HSVC_ABSENT UINT64_C(0) /* `value` of [`op::RECORDS_GET`]: no such record. */
Expand Down Expand Up @@ -606,6 +614,13 @@ extern "C" {
#define BB_HSVC_MAX_RANDOM_FILL UINT64_C(1024) /* The most bytes one `random.fill` answers. */
#define BB_HSVC_CONTENT_PASS UINT64_C(0) /* `content.scan`: the content passes. */
#define BB_HSVC_CONTENT_BLOCK UINT64_C(1) /* `content.scan`: the gate blocked it. */
#define BB_HSVC_DISK_ROTATED UINT8_C(1) /* [`DiskWritten::rotated`]: the host rotated the file before appending. */
#define BB_HSVC_DISK_RETENTION_FAILED UINT8_C(1) /* [`DiskWritten::faults`]: dropping the oldest archive failed (the archive series may exceed the */
#define BB_HSVC_DISK_SHIFT_FAILED UINT8_C(2) /* [`DiskWritten::faults`]: shifting an archive up one slot failed (it was left in place). */
#define BB_HSVC_DISK_RENAME_FAILED UINT8_C(4) /* [`DiskWritten::faults`]: renaming the live file to its first archive failed (the append went to */
#define BB_HSVC_DISK_FAULTS UINT8_C(7) /* Every [`DiskWritten::faults`] bit. */
#define BB_HSVC_DISK_OPEN_FAILED UINT64_C(1) /* A FAILED `disk.append`'s `ServiceOut::value`: the file could not be opened for the append. */
#define BB_HSVC_DISK_APPEND_FAILED UINT64_C(2) /* A FAILED `disk.append`'s `ServiceOut::value`: the file opened, and writing the bytes failed. */

/* ---- enumerations ---- */
/* What an op answered. */
Expand Down Expand Up @@ -955,6 +970,8 @@ typedef struct bb_hsvc_RandomFillIn bb_hsvc_RandomFillIn;
typedef struct bb_hsvc_ContentScanIn bb_hsvc_ContentScanIn;
typedef struct bb_hsvc_HookCallIn bb_hsvc_HookCallIn;
typedef struct bb_hsvc_NeedAdmitIn bb_hsvc_NeedAdmitIn;
typedef struct bb_hsvc_DiskAppendIn bb_hsvc_DiskAppendIn;
typedef struct bb_hsvc_DiskWritten bb_hsvc_DiskWritten;
typedef struct bb_hsvc_HostSlots bb_hsvc_HostSlots;

/* ---- scalar and function-pointer types ---- */
Expand Down Expand Up @@ -3482,6 +3499,23 @@ struct bb_hsvc_NeedAdmitIn {
uint32_t _reserved;
};

/* [`op::DISK_APPEND`]'s `in`: append `bytes` to the local file the host maps the calling */
struct bb_hsvc_DiskAppendIn {
bb_hsvc_ServiceHead head;
bb_mech_AbiStr dest_key;
bb_mech_Blob bytes;
bb_hsvc_DiskWritten *result;
};

/* `disk.append`'s result, written by the host into [`DiskAppendIn::result`] on READY and FAILED. */
struct bb_hsvc_DiskWritten {
uint32_t size;
uint8_t rotated;
uint8_t faults;
uint8_t _reserved[2];
uint64_t written;
};

/* THE HOST SERVICES TABLE: one [`ServiceFn`] per [`op`], in index order. A NULL slot is a service */
struct bb_hsvc_HostSlots {
uint32_t size;
Expand All @@ -3508,9 +3542,10 @@ struct bb_hsvc_HostSlots {
bb_hsvc_ServiceFn need_admit;
bb_hsvc_ServiceFn trust_verify;
bb_hsvc_ServiceFn records_secret;
bb_hsvc_ServiceFn disk_append;
};

/* ---- layout proof: 260 of 263 structures are pinned by the golden ---- */
/* ---- layout proof: 262 of 265 structures are pinned by the golden ---- */
#if UINTPTR_MAX == UINT64_MAX
#ifdef __cplusplus
#define BB_ASSERT(c, m) static_assert(c, m)
Expand Down Expand Up @@ -5480,7 +5515,20 @@ BB_ASSERT(BB_ALIGNOF(bb_hsvc_NeedAdmitIn) == 4, "bb_hsvc_NeedAdmitIn: alignment"
BB_ASSERT(offsetof(bb_hsvc_NeedAdmitIn, head) == 0, "bb_hsvc_NeedAdmitIn.head: offset");
BB_ASSERT(offsetof(bb_hsvc_NeedAdmitIn, need) == 24, "bb_hsvc_NeedAdmitIn.need: offset");
BB_ASSERT(offsetof(bb_hsvc_NeedAdmitIn, _reserved) == 28, "bb_hsvc_NeedAdmitIn._reserved: offset");
BB_ASSERT(sizeof(bb_hsvc_HostSlots) == 184, "bb_hsvc_HostSlots: size");
BB_ASSERT(sizeof(bb_hsvc_DiskAppendIn) == 72, "bb_hsvc_DiskAppendIn: size");
BB_ASSERT(BB_ALIGNOF(bb_hsvc_DiskAppendIn) == 8, "bb_hsvc_DiskAppendIn: alignment");
BB_ASSERT(offsetof(bb_hsvc_DiskAppendIn, head) == 0, "bb_hsvc_DiskAppendIn.head: offset");
BB_ASSERT(offsetof(bb_hsvc_DiskAppendIn, dest_key) == 24, "bb_hsvc_DiskAppendIn.dest_key: offset");
BB_ASSERT(offsetof(bb_hsvc_DiskAppendIn, bytes) == 40, "bb_hsvc_DiskAppendIn.bytes: offset");
BB_ASSERT(offsetof(bb_hsvc_DiskAppendIn, result) == 64, "bb_hsvc_DiskAppendIn.result: offset");
BB_ASSERT(sizeof(bb_hsvc_DiskWritten) == 16, "bb_hsvc_DiskWritten: size");
BB_ASSERT(BB_ALIGNOF(bb_hsvc_DiskWritten) == 8, "bb_hsvc_DiskWritten: alignment");
BB_ASSERT(offsetof(bb_hsvc_DiskWritten, size) == 0, "bb_hsvc_DiskWritten.size: offset");
BB_ASSERT(offsetof(bb_hsvc_DiskWritten, rotated) == 4, "bb_hsvc_DiskWritten.rotated: offset");
BB_ASSERT(offsetof(bb_hsvc_DiskWritten, faults) == 5, "bb_hsvc_DiskWritten.faults: offset");
BB_ASSERT(offsetof(bb_hsvc_DiskWritten, _reserved) == 6, "bb_hsvc_DiskWritten._reserved: offset");
BB_ASSERT(offsetof(bb_hsvc_DiskWritten, written) == 8, "bb_hsvc_DiskWritten.written: offset");
BB_ASSERT(sizeof(bb_hsvc_HostSlots) == 192, "bb_hsvc_HostSlots: size");
BB_ASSERT(BB_ALIGNOF(bb_hsvc_HostSlots) == 8, "bb_hsvc_HostSlots: alignment");
BB_ASSERT(offsetof(bb_hsvc_HostSlots, size) == 0, "bb_hsvc_HostSlots.size: offset");
BB_ASSERT(offsetof(bb_hsvc_HostSlots, slots) == 4, "bb_hsvc_HostSlots.slots: offset");
Expand All @@ -5506,6 +5554,7 @@ BB_ASSERT(offsetof(bb_hsvc_HostSlots, random_fill) == 152, "bb_hsvc_HostSlots.ra
BB_ASSERT(offsetof(bb_hsvc_HostSlots, need_admit) == 160, "bb_hsvc_HostSlots.need_admit: offset");
BB_ASSERT(offsetof(bb_hsvc_HostSlots, trust_verify) == 168, "bb_hsvc_HostSlots.trust_verify: offset");
BB_ASSERT(offsetof(bb_hsvc_HostSlots, records_secret) == 176, "bb_hsvc_HostSlots.records_secret: offset");
BB_ASSERT(offsetof(bb_hsvc_HostSlots, disk_append) == 184, "bb_hsvc_HostSlots.disk_append: offset");
#endif

#ifdef __cplusplus
Expand Down
6 changes: 3 additions & 3 deletions crates/busbar-contract/src/abi/cold/auth.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2026 Busbar Inc and contributors

//! The **auth** payload schema (kind = [`crate::abi::cold::kind::AUTH`]) that rides the kind-neutral `call`.
//! The **auth** payload schema (kind = `auth`) that rides the kind-neutral `call`.
//!
//! ## Identity-only — STRUCTURAL, not merely conventional
//!
Expand Down Expand Up @@ -258,13 +258,13 @@ pub struct HttpResponse {

impl HttpResponse {
/// The [`status`](Self::status), VALIDATED via
/// [`crate::abi::cold::endpoint::safe_relay_status`] — a real HTTP status code, or `502` when the
/// [`crate::abi::mechanism::endpoint::safe_relay_status`] — a real HTTP status code, or `502` when the
/// value is out of range. THE safe conversion for any host path that turns this plugin-chosen
/// status into a `StatusCode`, so an attacker-chosen `0`/`65535` can never panic a naive
/// `from_u16(status).unwrap()`.
#[must_use]
pub fn safe_status(&self) -> u16 {
crate::abi::cold::endpoint::safe_relay_status(self.status)
crate::abi::mechanism::endpoint::safe_relay_status(self.status)
}
}

Expand Down
18 changes: 9 additions & 9 deletions crates/busbar-contract/src/abi/cold/export.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2026 Busbar Inc and contributors

//! The **export** payload schema (kind = [`crate::abi::cold::kind::EXPORT`]) that rides the kind-neutral `call`.
//! The **export** payload schema (kind = `export`) that rides the kind-neutral `call`.
//!
//! ## One transport, every export op
//!
Expand All @@ -19,11 +19,11 @@
//! - `deliver` — hand one already-serialized batch for a declared stream to the sink. The payload is
//! carried as an opaque [`serde_json::Value`] the engine built; the export ABI adds the envelope,
//! never a second copy of the batch semantics.
//! - `routes` / `http_endpoint` — the sink's HTTP surface (see [`crate::abi::cold::endpoint`]).
//! - `routes` / `http_endpoint` — the sink's HTTP surface (see [`crate::abi::mechanism::endpoint`]).
//! - `status` — what the sink has to report when the host renders its exposition (additive).

use crate::abi::cold::endpoint::{EndpointRequest, EndpointResponse};
use crate::abi::export::{CheckPhase, ExportStream};
use crate::abi::mechanism::endpoint::{EndpointRequest, EndpointResponse};
use crate::abi::mechanism::route::Route;
use serde::{Deserialize, Serialize};

Expand All @@ -34,11 +34,11 @@ use serde::{Deserialize, Serialize};
/// A REMOVED wire token is a breaking payload change, so the floor moves rather than accepting a
/// token the engine can no longer route. This is the per-kind PAYLOAD axis, NOT the transport axis
/// — an export plugin exports the SAME six neutral symbols ([`crate::abi::cold::symbol`]) as every other kind, at
/// `busbar_abi() == TRANSPORT_VERSION`. Named the same way [`crate::abi::cold::SECRET_ABI_VERSION`] is, so the loader floor and the SDK's declared version share one
/// `busbar_abi() == TRANSPORT_VERSION`. Named the same way the other kinds' versions are, so the loader floor and the SDK's declared version share one
/// const and cannot silently drift apart.
///
/// v2 -> v3 (1.6.0, DECISIONS #85 — THE OBSERVABILITY ENVELOPE): an export response is now
/// [`crate::abi::cold::observe::Envelope`]`<ExportResponse>` — `{ result, metrics[], diagnostics[] }` —
/// [`crate::abi::mechanism::observe::Envelope`]`<ExportResponse>` — `{ result, metrics[], diagnostics[] }` —
/// instead of a bare `ExportResponse`. **This is the bump #85 called for and the only one it
/// required.**
///
Expand Down Expand Up @@ -83,10 +83,10 @@ pub const EXPORT_ABI_VERSION: u32 = 3;
///
/// 0: `streams` / `deliver` / `routes` / `http_endpoint` / `status` (the v3 surface).
/// 1 (K9a S1): the FIRST-PARTY METRIC NAMESPACE — a manifest's `declares.metrics`
/// ([`crate::abi::cold::observe::SeriesDecl`]).
/// ([`crate::abi::mechanism::observe::SeriesDecl`]).
/// 2 (K9a S2): the `validate` op ([`ExportRequest::Validate`] / [`ExportResponse::Validated`]).
/// 3 (K9a S3): PLUGIN DIAGNOSTICS — a manifest's `declares.diagnostics`
/// ([`crate::abi::cold::observe::DiagnosticDecl`]).
/// ([`crate::abi::mechanism::observe::DiagnosticDecl`]).
/// 4 (K9a S4): the DESTINATION HANDLE — a manifest's `declares.destinations`, the host-executed
/// [`HostOp`]s a delivery may answer with ([`ExportResponse::Host`]), and the op that resumes it
/// with their [`HostResult`]s ([`ExportRequest::Resume`]).
Expand All @@ -96,7 +96,7 @@ pub const EXPORT_ABI_VERSION: u32 = 3;
/// samples as [`MetricFamily`]s; the sink answers the exposition it renders
/// ([`ExportResponse::Exposition`]).
/// 7 (K9b): the SHED COUNTER — a declared series may be marked `shed`
/// ([`crate::abi::cold::observe::SeriesDecl::shed`]): the host counts on it each delivery it sheds for
/// ([`crate::abi::mechanism::observe::SeriesDecl::shed`]): the host counts on it each delivery it sheds for
/// the sink, which the sink is never called for and so cannot count.
/// 8 (K9c): the START op ([`ExportRequest::Start`] / [`ExportResponse::Started`]) — the host
/// starts feeding the sink and it states its in-flight admission; the CHECK op
Expand Down Expand Up @@ -427,7 +427,7 @@ pub enum ExportResponse {
#[serde(rename = "Http")]
Endpoint(EndpointResponse),
/// `status` — what the sink observed since it last reported, in the observability envelope's
/// own entry shapes (`crate::abi::cold::observe::PluginMetric` /
/// own entry shapes (`crate::abi::mechanism::observe::PluginMetric` /
/// `PluginDiagnostic` as JSON values), so the host runs them through the SAME validator and fold
/// it runs the envelope's arrays through. Either list may be empty or absent on the wire.
Status {
Expand Down
Loading
Loading