Custom domain contracts (spike)
Architecture notes on app-defined RPC/events alongside Shardwire: why they are not first-class in core today, and what would have to be decided before they could be.
This page is a spike / ADR-style note for maintainers and advanced integrators. It answers: “Could Shardwire carry my own music.play actions like built-in Discord actions?”
Current charter: Shardwire is a Discord-first bridge—negotiation, manifests, scoped secrets, strict startup, and diagnostics are all Discord-catalog-shaped (Product boundaries, Capabilities & scoped secrets).
Proposal (external)
Allow arbitrary domain envelopes on the same wire, for example:
music.play,queue.updated,player.state- schema validation (e.g. Zod) and codegen
- manifest + secret scope + strict startup extended to those names
That would move the product toward a generic split-process application bus.
Why this is not in core today
-
Negotiation model — Today, negotiated
events/actionsare drawn from a fixed catalog aligned with Discord intents and bot-side execution. Arbitrary names require a second negotiation channel, versioning, and forward compatibility rules. -
Secret scope semantics —
generateSecretScopeandexpectedScopeexpress Discord capability minima/maxima. Mapping “secret allowsmusic.seek” into the sameSecretPermissionsshape either overloads meaning or forks the type system. -
Strict startup and diagnosis —
diagnoseShardwireAppencodes Discord-specific rules (intents, filter metadata keys, subscription vs manifest). Domain RPC would need a parallel diagnosis system or risk mixing incompatible issue codes. -
Security and abuse — Built-in actions ultimately map to audited Discord runtime code paths. User-defined RPC would require sandboxing, payload limits, authz between apps, and clear threat models—platform scope.
-
Operational story — Replay, buffering, and resumable sessions (often requested with custom contracts) imply persistence and ordering guarantees Shardwire does not promise today.
Recommended patterns (without core changes)
-
Second channel for domain state — Keep Shardwire for Discord only; use HTTP, Redis, or a small dedicated WebSocket for
music.*payloads between your dashboard and a co-located app or API. The music controller recipe shows this split. -
Optional experimental package — If you prototype
defineShardwireContract, do not reusemanifest.events/server.secrets.allowfor non-Discord symbols until negotiation, auth, and codegen are specified in a standalone design doc.
If core ever adopts a subset
A minimal, charter-safe subset might look like: documented envelopes carried only after explicit opt-in at both ends, with no merging into generateSecretScope / strict startup until ADR sign-off. Anything broader should stay in a separate package and release cadence.
Related
- How Shardwire works
- Optional
@shardwire/reactcompanion package in this repository (packages/react) for dashboard-facing hooks