feat(v1.1.6): realtime channels + v1.1.5 follow-ups + version bumps
Server-side realtime SSE on per-app pub/sub topics, plus the three
v1.1.5 follow-ups and the version bumps.
Realtime:
- topics registry (0021) + admin endpoints + Capability::AppTopicManage
(-> app:admin; no new scope).
- GET /realtime/topics/{topic} SSE endpoint (orchestrator-core data
plane): Host -> app, RealtimeAuthority gate (404 missing/internal,
401 bad/absent token), broadcast::Receiver stream + heartbeat.
- RealtimeBroadcaster / RealtimeEvent / RealtimeAuthority traits
(picloud-shared); InProcessBroadcaster + GC (orchestrator-core);
DB-backed RealtimeAuthorityImpl (manager-core). Publish path fans out
to in-process subscribers after the durable outbox commit (best-effort,
panic-isolated).
- HMAC subscriber tokens (subscriber_token.rs) + app_secrets table (0022)
+ pubsub::subscriber_token SDK (schema 1.6 -> 1.7). TTL clamp + env
overrides.
- Dashboard Topics tab (register/list/edit/delete, prominent external
badge, flip confirmation).
v1.1.5 follow-ups:
- Empty blobs accepted (NewFile/FileUpdate::validate) + round-trip test.
- Orphan *.tmp.* sweeper (spawn_files_orphan_sweep).
- Dispatcher e2e tests, one per trigger kind (DATABASE_URL-gated).
Versions: workspace 1.1.6, SDK 1.7, dashboard 0.12.0. Schema-snapshot
golden re-blessed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -30,6 +30,32 @@ pub trait PubsubService: Send + Sync {
|
||||
topic: &str,
|
||||
message: serde_json::Value,
|
||||
) -> Result<(), PubsubError>;
|
||||
|
||||
/// Mint an HMAC-signed realtime subscriber token (v1.1.6). Backs the
|
||||
/// `pubsub::subscriber_token(topics, ttl)` Rhai SDK call. The minted
|
||||
/// token authorizes an external SSE client to subscribe to the given
|
||||
/// `topics` for `ttl_seconds` (clamped to the configured bounds; the
|
||||
/// configured default applies when `ttl_seconds` is `None`).
|
||||
///
|
||||
/// Every topic must already be registered as externally subscribable
|
||||
/// in `cx.app_id`; `cx.principal` must be `Some` (anonymous
|
||||
/// public-HTTP scripts can't mint). See [`PubsubError::SubscriberToken`]
|
||||
/// for the rejection messages.
|
||||
///
|
||||
/// The default impl errors `Unavailable` so test fakes and the
|
||||
/// `NoopPubsubService` keep compiling; the real minting lives in
|
||||
/// manager-core's `PubsubServiceImpl`.
|
||||
async fn mint_subscriber_token(
|
||||
&self,
|
||||
cx: &SdkCallCx,
|
||||
topics: Vec<String>,
|
||||
ttl_seconds: Option<i64>,
|
||||
) -> Result<String, PubsubError> {
|
||||
let _ = (cx, topics, ttl_seconds);
|
||||
Err(PubsubError::Unavailable(
|
||||
"subscriber tokens are not wired in".into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
@@ -47,6 +73,13 @@ pub enum PubsubError {
|
||||
#[error("pubsub rejected: {0}")]
|
||||
Rejected(String),
|
||||
|
||||
/// A `pubsub::subscriber_token` mint was rejected (empty topics,
|
||||
/// unregistered topic, ttl out of range, anonymous caller). The
|
||||
/// string is the full user-facing message; the SDK surfaces it
|
||||
/// verbatim so scripts see the documented wording.
|
||||
#[error("{0}")]
|
||||
SubscriberToken(String),
|
||||
|
||||
/// Anything else — Postgres unavailable, etc.
|
||||
#[error("pubsub backend error: {0}")]
|
||||
Unavailable(String),
|
||||
|
||||
Reference in New Issue
Block a user