Quickstart
Early release (v0.2). APIs and deployment guidance may still change, and dotwire isn't production-hardened yet, so evaluate it carefully before relying on it. Feedback and issues are welcome at github.com/pasztor-mark/dotwire.
In this guide you run dotwire, Postgres (TimescaleDB), and NATS on your machine with Docker Compose. Then you use the Node.js host SDK to grant a user access to a room, mint a token, and send a message.
Prerequisites
- Docker with Docker Compose
- OpenSSL, to generate keys
- Git
- Node.js 20 or later, for the host SDK step
1. Clone the repository
git clone https://github.com/pasztor-mark/dotwire.git
cd dotwire
cp .env.example .env
.env is gitignored. .env.example is the template.
2. Generate keys
You need two unrelated keys. One is dotwire's own at-rest encryption key. The other is an RSA keypair that stands in for your host application's JWT signing key.
# AES-256-GCM at-rest encryption key (32 bytes, base64)
openssl rand -base64 32
# RS256 keypair. The private key stays outside dotwire; it plays the host.
openssl genpkey -algorithm RSA -out dev-jwt.key -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in dev-jwt.key -out dev-jwt.pub
See Encryption and retention for why these are separate key systems.
3. Fill in .env
Set these values in .env:
POSTGRES_PASSWORD=<migrator-password>
POSTGRES_APP_PASSWORD=<app-password>
Dotwire__Encryption__ActiveKeyId=k1
Dotwire__Encryption__Keys__k1=<base64-32-byte-key>
Auth__Issuer=dev-host
Auth__Audience=dotwire
Auth__Keys__dev="-----BEGIN PUBLIC KEY-----\n<public-key-body>\n-----END PUBLIC KEY-----"
POSTGRES_PASSWORDis for thedotwireowner role, which runs migrations.POSTGRES_APP_PASSWORDis for thedotwire_appruntime role.Dotwire__Encryption__Keys__k1is the output ofopenssl rand -base64 32.- The suffix after
Auth__Keys__is the key id (kid).compose.yamlpassesAuth__Keys__devto the container, so keep the key iddev. - The public key must be one double-quoted line, with
\nin place of each line break. This prints the finished line fromdev-jwt.pub:
echo "Auth__Keys__dev=\"$(awk '{printf "%s\\n",$0}' dev-jwt.pub)\""
4. Start the stack
docker compose up -d --build
Omit --build to run the published image, ghcr.io/pasztor-mark/dotwire:latest (linux/amd64 and linux/arm64), instead of building from source.
On startup, dotwire runs its database migrations and provisions its JetStream streams.
5. Check that dotwire is up
curl http://localhost:8080/healthz
A 200 OK response means the service is up.
6. Grant access, mint a token, and send a message
In a separate folder, install the host SDK:
npm install @dotwire/hostCreate first-message.mjs. Point privateKeyPem at the dev-jwt.key you generated in step 2.
import { readFileSync } from 'node:fs';
import { DotwireHostClient } from '@dotwire/host';
const host = new DotwireHostClient({
baseUrl: 'http://localhost:8080',
privateKeyPem: readFileSync('../dotwire/dev-jwt.key', 'utf8'),
keyId: 'dev',
issuer: 'dev-host',
audience: 'dotwire'
});
const roomId = 'a0000000-0000-0000-0000-000000000001';
await host.setUserRole('user-1', 'member');
await host.addRoomMembers(roomId, ['user-1']);
const token = await host.mintToken('user-1', { role: 'member', ttl: '15m' });
const ack = await host.sendAsUser(roomId, 'user-1', 'Hello, dotwire');
console.log(`Accepted at seq ${ack.seq}`);
const history = await host.getMessages(roomId, { asUserId: 'user-1', limit: 50 });
console.log(`History has ${history.messages.length} message(s)`);
Run it:
node first-message.mjs
What happens:
setUserRoleandaddRoomMemberscall the admin API. The SDK signs those calls ashost-admin, a user that the migrations seed with theadminrole.mintTokenreturns the RS256 JWT a browser client would use foruser-1. In a real app, your token endpoint returns it to@dotwire/client.sendAsUsermints its own token foruser-1and posts the message.ack.seqis the message's position in the room's ordering.getMessagesreads the room's history asuser-1, who must be a room member.
Room ids are UUIDs. A room exists as soon as it has members; there is no separate create step.
Stop the stack
docker compose down
This keeps the pgdata and natsdata volumes. Add -v to delete them and start fresh.
Next steps
- Integrate a host app: token endpoint, browser client, and participant lookup.
- Architecture: what "accepted" means and how live delivery works.
- Deployment: node roles, NATS, and running more than one node.