plugins/lisa-harper-fabric/skills/harper-realtime/SKILL.md
This skill should be used when adding or troubleshooting Harper (HarperDB/Fabric) real-time behavior: MQTT topics, WebSocket resource subscriptions, resource publish/subscribe handlers, SSE-style streaming routes, and local subscriber verification. Pairs with harper-resources, harper-config-yaml, harper-schema-graphql, and harper-build-and-deploy.
npx skillsauth add codyswanngt/lisa harper-realtimeInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Harper exposes live data through the same Resource model used for REST and GraphQL. Exported tables/resources can be addressed as MQTT topics and WebSocket paths, while custom resources can override the messaging handlers when the default table behavior is not enough.
Use real-time primitives when the product needs pushed state: live feeds, activity streams, collaborative views, device telemetry, or status updates. Do not replace these with client polling unless the issue explicitly asks for polling.
@table @export creates a default exported table resource.resources.js creates a resource
endpoint with the class name as the path/topic root.resource/# for a resource
subtree and resource/+/status for one path segment.ws://localhost:9926/Activity/123 subscribes to the Activity resource record
with id 123.Keep the URL/topic names stable. If a UI depends on Activity/123, changing the
resource export name is an API break.
The MQTT plugin routes broker messages through Resource methods:
| Method | Use |
| --- | --- |
| subscribe(target, context) | Authorize and shape subscription reads for a resource or record. |
| publish(target, message, context) | Accept or transform messages written to a topic/resource. |
| connect(incomingMessages) | Customize WebSocket connection behavior for a resource. |
For table-backed resources, prefer default behavior until the product needs a
custom message shape, authorization check, fan-out, or derived event. When
overriding, preserve built-in table behavior with super.subscribe(...) or
super.publish(...) where that behavior is still wanted.
export class Activity extends tables.Activity {
static async publish(target, message, context) {
const payload = await message;
await super.publish(target, payload, context);
return { ...payload, acceptedAt: Date.now() };
}
}
For WebSocket-only behavior, implement connect(incomingMessages) and return an
async iterable. Use the default super.connect() stream when you only need to
push extra server messages or clean up on disconnect.
| Transport | Best fit | Notes |
| --- | --- | --- |
| MQTT | Device streams, backend subscribers, wildcard topics, durable clients. | Plain MQTT defaults to port 1883; MQTTS defaults to 8883. |
| MQTT over WebSocket | Browser or edge clients using MQTT semantics. | Uses the HTTP port, default 9926, with the mqtt WebSocket subprotocol. |
| Resource WebSocket | Browser live views tied to one REST resource path. | Enabled with the rest plugin unless rest.webSocket: false is set. |
| SSE/custom streaming route | One-way browser updates when WebSocket is not appropriate. | Implement as a project route; do not assume SSE automatically subscribes to Resource changes. |
Authentication is usually required for MQTT. When mqtt.requireAuthentication is
false, authorization still applies at the resource/table level, so public
connections must be explicitly allowed by roles or resource logic.
Component-level config.yaml enables the app surface:
rest: true
graphqlSchema:
files: schema.graphql
jsResource:
files: resources.js
The root Harper harper-config.yaml, not the component config.yaml, owns broker
ports and MQTT authentication:
mqtt:
network:
port: 1883
securePort: 8883
webSocket: true
requireAuthentication: true
Keep the rest, graphqlSchema, and jsResource declarations together when a
project has a custom component config.yaml. Harper does not merge custom
component config with defaults. See [[harper-config-yaml]].
Resource/<id>) when the UI watches one
entity; use collection or wildcard subscriptions only when the product needs a
feed.For clustered/Fabric deployments, real-time behavior must match the deploy target:
replicated=true when deploying a component that needs to run across Fabric
nodes. See [[harper-build-and-deploy]].Build generated Harper assets from source:
bun run build
Start the component:
harper dev harper-app
In another shell, subscribe to a topic or WebSocket path:
npx mqtt sub -h localhost -p 1883 -u HDB_ADMIN -P "$HARPER_PASSWORD" -t 'Activity/#' -v
npx wscat -c ws://localhost:9926/Activity/123
Publish or mutate the watched record:
npx mqtt pub -h localhost -p 1883 -u HDB_ADMIN -P "$HARPER_PASSWORD" -t 'Activity/123' -m '{"status":"live"}'
Confirm the subscriber receives the expected message shape and that a normal REST/GraphQL read returns the same resulting state.
Record the command, topic/path, payload, and observed message in PR evidence. If a local Harper binary or credentials are unavailable, report that blocker instead of claiming the subscription was verified.
src/; harper-app/resources.js is a
generated artifact. See [[harper-resources]].config.yaml, update the project Fabric runbook and
re-run the smoke path. See [[harper-config-yaml]].plugins/src/harper-fabric, run
bun run build:plugins, and commit both source and generated plugin copies.development
Prepare a machine — a fresh laptop or a throwaway container — to run coding agents, before any repository exists. Detects which of Lisa's supported agents (Claude Code, Codex, Cursor, OpenCode, Antigravity, Copilot) are already installed, asks which credential manager the machine uses (Bitwarden, 1Password, Doppler, Vault, AWS, or none), and installs only what is missing, each by its vendor's own preferred method. Idempotent, headless by default, and emits a Dockerfile for a spin-up/spin-down environment. Run it on a new machine, in a container, or before cloning anything.
tools
Provision and verify a remote execution environment for a host project — Codex Cloud today, other remote surfaces as they are added. Generates a repository-owned setup script that installs the declared toolchain, materializes secrets through lisa-secrets-access, and runs the project's own hook. Provisions by API where one exists, by driving the vendor console where one does not, and by emitting exact config otherwise — then proves the result with the same read-back regardless of which tier did the work. Use before dispatching any work with executionEnv.
tools
Bring a developer's machine in line with the toolchain the project declares. Reports every tool in remoteEnv.tools that is missing, outdated, or unpinned for this platform, and installs the missing ones into ~/.local/bin from the same pinned, checksummed entries the remote surfaces use — but only when asked. Same manifest, same pins, same installers as lisa-setup-remote-env; what differs is consent and that the pin is a floor rather than an equality. Run it on a fresh checkout, after a manifest change, or when a tool fails at the moment of use.
tools
Route one unit of work to a remote execution surface. Reads the executionEnv parameter (local by default, codex-cloud or claude-web today), verifies the environment is provisioned and bound to this repository, submits a thin skill invocation, records the task identifier to .lisa/remote-dispatch.json, and exits without polling. Routing only — the remote runs the identical skill from the identical repository. Composable and inline: other skills invoke it via the Skill tool rather than users calling it directly.