@pugi/plugin-cloud-sync
v0.1.0-alpha.2
Published
Pugi cloud-sync plugin - bridges sessions to a yjs-relay backend for laptop and web continuation via yjs CRDT over Hocuspocus.
Maintainers
Readme
@pugi/plugin-cloud-sync
Pugi cloud-sync plugin. Bridges Pugi sessions to a Pugi-operated Hocuspocus relay so a session started on a laptop CLI can be picked up in the web Console (and vice versa) via yjs CRDT.
Part of the Pugi 1.0 soft fork sprint (see ADR-0081).
Targets the laptop-kickoff plus web-continue workflow on the Team tier ($199 / mo). Customer starts a session in the CLI, walks to the office, opens app.pugi.io, picks up where they left off.
Hook surface
| Hook | Purpose |
|---|---|
| chat.message | Append user turn to messages Y.Array |
| chat.params | Update active_persona Y.Text on persona switch |
| tool.execute.after | Append job to jobs Y.Array; record file mutation on file-touching tools |
| dispose | Flush final metadata and tear down every transport |
Slash commands
| Tool | Purpose |
|---|---|
| pugi.cloud.status | Returns connected state + peer count + tier gate decision |
| pugi.cloud.handoff | Generate a one-time deep link for web or mobile (5 min default lifetime) |
| pugi.cloud.disconnect | Tear down the cloud transport. Local edits queue for replay |
| pugi.cloud.pause | Pause sync without dropping the connection |
| pugi.cloud.resume | Resume sync after a pause or disconnect |
yjs document schema
| Root key | Type | Purpose |
|---|---|---|
| messages | Y.Array<YMessage> | Append-only chat history |
| file_mutations | Y.Map<YFileMutation> | Last-write-wins per file path |
| active_persona | Y.Text | Current persona slug, last-write-wins |
| metadata | Y.Map<YMetadata> | Session lifecycle info |
| jobs | Y.Array<YJob> | Tool job queue for cross-device continuation |
Document name is tenant-scoped server-side:
tenant_<sha256-of-bearer>/pugi-session-<sessionId>. Cross-tenant access
is impossible because the relay validates the prefix against the bearer
token.
Consistency model
Eventually consistent, not strongly consistent. Document tradeoffs:
- Lag: 50 to 500 ms typical, up to 5 s on poor networks.
- Concurrent edits: CRDT-merged automatically, no operator wait.
- Y.Array appends: commutative; both writes survive.
- Y.Map writes: last-write-wins by vector clock.
- Y.Text inserts: concurrent inserts merge cleanly.
- Tool job dedup: best-effort via hash; may execute twice if both
devices online simultaneously and call the same tool within 100 ms
(rare, surfaced via
cloud-sync:conflictevent). - File mutations: last-write-wins per path; concurrent edits to the same path surface a warning.
Tier matrix
| Tier | Cloud sync | Concurrent sessions | |---|---|---| | Free | No | 0 | | Founder ($20) | No | 0 | | Builder ($99) | Yes | 1 | | Team ($199) | Yes | 10 | | Enterprise | Yes | Unlimited per contract |
Client-side gate AND server-side gate, defense in depth. Ineligible tiers see an upgrade hint and no transport opens.
Threat model
- Bearer token: required for relay handshake. Anvil-side
/api/auth/validateresolves the tenant; document name carries the tenant prefix so the relay-side ACL rejects cross-tenant subscribes. - PII in presence: presence metadata is PII-free by default. Operator
display name is
pugi-cli@<hostname>; device class is a coarse string. - Handoff token: 32 bytes of crypto-random entropy, base64url encoded, 5 min default lifetime, clamped to [10s, 30min]. Plugin generates; admin-api persists.
- Local persistence: optional peer dep (
y-leveldb), opt-in vialocalPersistencePath. Logs stay PII-free.
Install
pnpm add @pugi/plugin-cloud-sync yjs @hocuspocus/providerUsage
// pugi.config.ts
import cloudSync from '@pugi/plugin-cloud-sync';
export default {
plugin: [
[
'@pugi/plugin-cloud-sync',
{
enableSync: true,
syncMode: 'auto',
// resolveTier defaults to 'free'; in production this delegates to
// the anvil-provider tier snapshot.
},
],
],
};License
MIT. See LICENSE.
