Skip to content

calternal_plugin::changes

One durable change sequence per User (DESIGN §§24, 58; #668).

This extends the Files feed’s transaction-and-wakeup model. Producers append IDs and revisions inside the transaction that publishes their projection or receipt. They wake the User only after commit. Push carries only a sequence; a lost wakeup is repaired by the stream’s periodic head check. The journal keeps at most 10,000 rows per User. Readers scan at most 100 rows, never an entire Home. After expiry or an access change, clients discard retained data and rescan only their bounded visible windows. No path is a public identity.

access_key is internal authority data, not a client projection. Each Plugin must check current access and return its current projection on every read. Missing adapters fail closed. This layer does not own caches or receipts.

Source: crates/calternal-plugin/src/changes.rs

pub struct Change

One current item revision or a deletion tombstone; bodies remain lazy.

Fields

  • pub sequence: i64
  • pub plugin_id: String
  • pub item_id: String
  • pub revision: String
  • pub deleted: bool
  • pub projection: Option<serde_json::Value>: A small header projection, produced by the owning Plugin after access checks.

Implements: Clone, Debug, Serialize, Deserialize, ToSchema

Source: crates/calternal-plugin/src/changes.rs:45

pub struct Page

One bounded catch-up page. An absent cursor request establishes a baseline.

Fields

  • pub entries: Vec<Change>
  • pub cursor: String
  • pub sequence: i64
  • pub has_more: bool

Implements: Debug, Serialize, Deserialize, ToSchema

Source: crates/calternal-plugin/src/changes.rs:102

pub struct Record

Internal journal row. Only an access-checked Change reaches a client.

Fields

  • pub sequence: i64
  • pub plugin_id: String
  • pub item_id: String
  • pub revision: String
  • pub deleted: bool
  • pub access_key: String

Implements: Clone, Debug

pub fn tombstone(&self) -> Change

A denied or deleted item retains only its previously granted identity.

Source: crates/calternal-plugin/src/changes.rs:34

pub struct Subscription

One User’s stream signal; multiple Installations share it, never other Users.

Fields

  • pub signal: Arc<Notify>

Implements: Drop

pub fn new(user: &str) -> Self

Subscribe before reading the head so a commit cannot race stream setup.

Source: crates/calternal-plugin/src/changes.rs:323

pub enum Error

Protocol errors are content-free and distinct from expired authority.

Variants

  • Invalid
  • Expired
  • Storage(sqlx::Error)

Implements: Debug, From<sqlx::Error>

Source: crates/calternal-plugin/src/changes.rs:89

pub type ProjectionFuture<'a> = std::pin::Pin<
Box<dyn std::future::Future<Output = Result<Option<Change>, sqlx::Error>> + Send + 'a>,
>;

Each namespace can add an access-checked header without duplicating protocol logic.

Source: crates/calternal-plugin/src/changes.rs:70

pub async fn append(
connection: &mut SqliteConnection,
user: &str,
record: &Record,
) -> Result<i64, Error>

Append at the producer’s commit boundary. Rollback removes the sequence too. Call wake_user after commit, never while the transaction is uncommitted.

Source: crates/calternal-plugin/src/changes.rs:123

pub async fn authority(db: &Db, user: &str, scope: &str) -> Result<String, sqlx::Error>

Include durable access epochs in cursor authority; instance toggles use one epoch.

Source: crates/calternal-plugin/src/changes.rs:170

pub async fn head(db: &Db, user: &str) -> Result<i64, sqlx::Error>

Read only this User’s high-water mark. Another User cannot advance it.

Source: crates/calternal-plugin/src/changes.rs:159

pub async fn install_files_bridge(db: &Db) -> Result<(), sqlx::Error>

Install the Files proving bridge after both migration sets exist. Existing Files-only consumers do not need the app journal (#668; DESIGN §§24, 58). Startup has one writer; installing twice leaves the same trigger contract. Sharing uses a read view and stored grants. Refresh grant triggers so an upgraded bridge invalidates Group members as well as direct Users (#867).

Source: crates/calternal-plugin/src/changes.rs:378

pub async fn invalidate_access(
connection: &mut SqliteConnection,
user: &str,
) -> Result<(), sqlx::Error>

Change the authority epoch in the same transaction as revoke or scope changes. Clients must discard all retained data before rebuilding bounded windows.

Source: crates/calternal-plugin/src/changes.rs:149

pub fn migrations() -> MigrationSet

Shared journal migrations use their own namespace, not a Plugin’s numbers.

Source: crates/calternal-plugin/src/changes.rs:110

pub async fn read<F>(
db: &Db,
key: &[u8; 32],
user: &str,
scope: &str,
token: Option<&str>,
project: F,
) -> Result<Page, Error>
where
F: for<'a> Fn(&'a Record) -> ProjectionFuture<'a>,

Fixed work read, even when every scanned row is denied. Recheck authority at the route after projection reads so a concurrent revoke cannot publish content.

Source: crates/calternal-plugin/src/changes.rs:218

pub fn wake_user(user: &str)

Notify only active streams for this User after durable commit. Periodic head checks repair missed notifications, including trigger-only publication.

Source: crates/calternal-plugin/src/changes.rs:362

pub const PAGE_BYTES: usize

Maximum encoded response, including the cursor and envelope.

Source: crates/calternal-plugin/src/changes.rs:28

pub const PAGE_ROWS: usize

Maximum journal rows scanned and returned by a single request.

Source: crates/calternal-plugin/src/changes.rs:26

pub const RETENTION_SECONDS: i64

Maximum time a cursor can cover without a bounded window rescan.

Source: crates/calternal-plugin/src/changes.rs:30