@pairlobby/local-server 0.0.0-stage → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/LICENSE +93 -0
  2. package/README.md +4 -2
  3. package/dist/backup.d.ts +79 -0
  4. package/dist/backup.d.ts.map +1 -0
  5. package/dist/backup.js +184 -0
  6. package/dist/backup.js.map +1 -0
  7. package/dist/check.d.ts +12 -0
  8. package/dist/check.d.ts.map +1 -0
  9. package/dist/check.js +102 -0
  10. package/dist/check.js.map +1 -0
  11. package/dist/index.d.ts +10 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +10 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/network.d.ts +23 -0
  16. package/dist/network.d.ts.map +1 -0
  17. package/dist/network.js +143 -0
  18. package/dist/network.js.map +1 -0
  19. package/dist/relay-main.d.ts +3 -0
  20. package/dist/relay-main.d.ts.map +1 -0
  21. package/dist/relay-main.js +141 -0
  22. package/dist/relay-main.js.map +1 -0
  23. package/dist/request-log.d.ts +38 -0
  24. package/dist/request-log.d.ts.map +1 -0
  25. package/dist/request-log.js +37 -0
  26. package/dist/request-log.js.map +1 -0
  27. package/dist/s3.d.ts +37 -0
  28. package/dist/s3.d.ts.map +1 -0
  29. package/dist/s3.js +140 -0
  30. package/dist/s3.js.map +1 -0
  31. package/dist/schema.d.ts +3 -0
  32. package/dist/schema.d.ts.map +1 -0
  33. package/dist/schema.js +98 -0
  34. package/dist/schema.js.map +1 -0
  35. package/dist/server.d.ts +60 -0
  36. package/dist/server.d.ts.map +1 -0
  37. package/dist/server.js +268 -0
  38. package/dist/server.js.map +1 -0
  39. package/dist/settings.d.ts +60 -0
  40. package/dist/settings.d.ts.map +1 -0
  41. package/dist/settings.js +172 -0
  42. package/dist/settings.js.map +1 -0
  43. package/dist/sqlite-store.d.ts +57 -0
  44. package/dist/sqlite-store.d.ts.map +1 -0
  45. package/dist/sqlite-store.js +331 -0
  46. package/dist/sqlite-store.js.map +1 -0
  47. package/package.json +43 -4
@@ -0,0 +1,143 @@
1
+ //! Who may reach a relay that listens beyond loopback, and how relays on one
2
+ //! local network find each other over mDNS (Bonjour), so a device can join with a
3
+ //! bare invite code. mDNS does not cross Tailscale; the CLI asks Tailscale for
4
+ //! its peers separately.
5
+ import { hostname, networkInterfaces } from 'node:os';
6
+ import makeMdns from 'multicast-dns';
7
+ export const RELAY_SERVICE = '_pairlobby._tcp.local';
8
+ function plainAddress(address) {
9
+ return address.toLowerCase().replace(/^::ffff:/, '');
10
+ }
11
+ /** Tailscale assigns addresses from 100.64.0.0/10 and fd7a:115c:a1e0::/48. */
12
+ export function isTailscaleAddress(address) {
13
+ const plain = plainAddress(address);
14
+ const v4 = /^(\d+)\.(\d+)\.\d+\.\d+$/.exec(plain);
15
+ if (v4) {
16
+ return Number(v4[1]) === 100 && (Number(v4[2]) & 0xc0) === 64;
17
+ }
18
+ return plain.startsWith('fd7a:115c:a1e0:');
19
+ }
20
+ export function isLoopbackAddress(address) {
21
+ const plain = plainAddress(address);
22
+ return plain === '::1' || plain.startsWith('127.');
23
+ }
24
+ /**
25
+ * This device, a private or link-local network address, or a Tailscale address:
26
+ * what may join a room by name when the room allows it. A request through a proxy
27
+ * arrives from the proxy, so a public URL in front of the relay is judged by the
28
+ * proxy's address; keep such rooms closed to the local network.
29
+ */
30
+ export function isLocalNetworkAddress(address) {
31
+ const plain = plainAddress(address);
32
+ if (isLoopbackAddress(plain) || isTailscaleAddress(plain)) {
33
+ return true;
34
+ }
35
+ const v4 = /^(\d+)\.(\d+)\.\d+\.\d+$/.exec(plain);
36
+ if (v4) {
37
+ const [first, second] = [Number(v4[1]), Number(v4[2])];
38
+ return first === 10 || (first === 172 && second >= 16 && second <= 31) || (first === 192 && second === 168) || (first === 169 && second === 254);
39
+ }
40
+ return /^f[cd][0-9a-f]{2}:/.test(plain) || /^fe[89ab][0-9a-f]:/.test(plain);
41
+ }
42
+ /** This machine's non-internal IPv4 addresses, optionally only its Tailscale ones. */
43
+ export function ownAddresses(tailscaleOnly = false) {
44
+ return Object.values(networkInterfaces()).flatMap((addresses) => (addresses ?? []).filter((address) => address.family === 'IPv4' && !address.internal && (!tailscaleOnly || isTailscaleAddress(address.address))).map((address) => address.address));
45
+ }
46
+ function localName() {
47
+ const name = hostname().replace(/\.local$/i, '').replace(/[^A-Za-z0-9-]/g, '-') || 'pairlobby';
48
+ return `${name}.local`;
49
+ }
50
+ /**
51
+ * Answers mDNS queries for PairLobby relays with this relay's port and current
52
+ * LAN addresses. Returns a function that stops answering.
53
+ */
54
+ export function advertiseRelay(port) {
55
+ let mdns;
56
+ try {
57
+ mdns = makeMdns();
58
+ }
59
+ catch {
60
+ return () => { };
61
+ }
62
+ const target = localName();
63
+ const instance = `PairLobby ${target.slice(0, -'.local'.length)} ${port}.${RELAY_SERVICE}`;
64
+ mdns.on('error', () => { });
65
+ mdns.on('query', (query) => {
66
+ if (!(query.questions ?? []).some((question) => question.name.toLowerCase() === RELAY_SERVICE && ['PTR', 'ANY'].includes(String(question.type)))) {
67
+ return;
68
+ }
69
+ const addresses = ownAddresses().filter((address) => !isTailscaleAddress(address));
70
+ if (addresses.length === 0) {
71
+ return;
72
+ }
73
+ mdns.respond({
74
+ answers: [{ name: RELAY_SERVICE, type: 'PTR', ttl: 120, data: instance }],
75
+ additionals: [
76
+ { name: instance, type: 'SRV', ttl: 120, data: { port, target, priority: 0, weight: 0 } },
77
+ { name: instance, type: 'TXT', ttl: 120, data: ['v=1'] },
78
+ ...addresses.map((address) => ({ name: target, type: 'A', ttl: 120, data: address })),
79
+ ],
80
+ });
81
+ });
82
+ return () => mdns.destroy();
83
+ }
84
+ /** Relay addresses announced on the local network within `timeoutMs`, as `http://ip:port` URLs. */
85
+ export function browseRelays(timeoutMs = 1500, signal) {
86
+ return new Promise((resolve) => {
87
+ if (signal?.aborted) {
88
+ resolve([]);
89
+ return;
90
+ }
91
+ let mdns;
92
+ try {
93
+ mdns = makeMdns();
94
+ }
95
+ catch {
96
+ resolve([]);
97
+ return;
98
+ }
99
+ const found = new Set();
100
+ mdns.on('error', () => { });
101
+ mdns.on('response', (response) => {
102
+ const records = [...(response.answers ?? []), ...(response.additionals ?? [])];
103
+ for (const record of records) {
104
+ if (record.type !== 'SRV' || !record.name.toLowerCase().endsWith(RELAY_SERVICE)) {
105
+ continue;
106
+ }
107
+ const { port, target } = record.data;
108
+ for (const address of records) {
109
+ if (address.type === 'A' && address.name.toLowerCase() === target.toLowerCase()) {
110
+ found.add(`http://${address.data}:${port}`);
111
+ }
112
+ }
113
+ }
114
+ });
115
+ let done = false;
116
+ const finish = () => {
117
+ if (!done) {
118
+ done = true;
119
+ clearTimeout(deadline);
120
+ signal?.removeEventListener('abort', finish);
121
+ mdns.destroy();
122
+ resolve([...found]);
123
+ }
124
+ };
125
+ const deadline = setTimeout(finish, timeoutMs);
126
+ signal?.addEventListener('abort', finish, { once: true });
127
+ // macOS refuses multicast (EHOSTUNREACH) to an app without the Local Network
128
+ // permission; nothing can answer then, so stop waiting.
129
+ const ask = () => mdns.query({ questions: [{ name: RELAY_SERVICE, type: 'PTR' }] }, (error) => {
130
+ if (error) {
131
+ finish();
132
+ }
133
+ });
134
+ ask();
135
+ // Like other mDNS clients, ask twice: the first query can leave before a responder is listening.
136
+ setTimeout(() => {
137
+ if (!done) {
138
+ ask();
139
+ }
140
+ }, Math.min(400, timeoutMs / 2)).unref();
141
+ });
142
+ }
143
+ //# sourceMappingURL=network.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network.js","sourceRoot":"","sources":["../src/network.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=relay-main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-main.d.ts","sourceRoot":"","sources":["../src/relay-main.ts"],"names":[],"mappings":""}
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env node
2
+ //! `pairlobby-relay`: the relay alone, for a container or a service manager. It takes the
3
+ //! same PAIRLOBBY_* settings as `pairlobby serve`, logs JSON lines, and has none of the
4
+ //! CLI's room commands, network sharing or local discovery.
5
+ import { readFileSync } from 'node:fs';
6
+ import { join } from 'node:path';
7
+ import { parseArgs } from 'node:util';
8
+ import { RelaySettingsError, relaySettings } from './settings.js';
9
+ const HELP = `pairlobby-relay: a PairLobby relay for a container or a service
10
+
11
+ pairlobby-relay run the relay with settings from PAIRLOBBY_* variables
12
+ pairlobby-relay check <url> check a deployed relay: health, invite address, create token,
13
+ then one test room through create, join, send, read and delete
14
+ pairlobby-relay --version
15
+
16
+ Settings (a flag wins over its variable)
17
+ --data-dir <dir> PAIRLOBBY_DATA_DIR required; where rooms.sqlite lives
18
+ --host <address> PAIRLOBBY_HOST default 127.0.0.1; 0.0.0.0 in a container
19
+ --port <port> PAIRLOBBY_PORT default 8790
20
+ --public-url <url> PAIRLOBBY_PUBLIC_URL the address invites show
21
+ --trusted-proxy-header PAIRLOBBY_TRUSTED_PROXY_HEADER such as x-forwarded-for, behind a load balancer
22
+ --request-log <mode> PAIRLOBBY_REQUEST_LOG all, writes or off (default)
23
+ --backup <place> PAIRLOBBY_BACKUP s3://bucket/prefix or an absolute folder
24
+ --no-restore PAIRLOBBY_NO_RESTORE=1 start empty on an empty disk
25
+ PAIRLOBBY_CREATE_TOKEN(_FILE) token needed to create rooms
26
+ See the guide for the rest: limits, allowed hosts, backup interval and S3 endpoint.
27
+ `;
28
+ function version() {
29
+ return JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version;
30
+ }
31
+ function log(line) {
32
+ process.stdout.write(`${JSON.stringify({ ts: new Date().toISOString(), ...line })}\n`);
33
+ }
34
+ async function check(url, json) {
35
+ if (!url || !/^https?:\/\//.test(url)) {
36
+ process.stderr.write('pairlobby-relay check needs the relay\'s address, such as https://pairlobby.internal.example.com\n');
37
+ return 2;
38
+ }
39
+ const { checkRelay } = await import('./check.js');
40
+ const createToken = process.env['PAIRLOBBY_CREATE_TOKEN'] || undefined;
41
+ const steps = await checkRelay(url, createToken ? { createToken } : {});
42
+ if (json) {
43
+ process.stdout.write(`${JSON.stringify(steps, null, 2)}\n`);
44
+ }
45
+ else {
46
+ for (const { name, ok, detail } of steps) {
47
+ process.stdout.write(`${ok ? 'ok ' : 'FAIL'} ${name.padEnd(14)} ${detail}\n`);
48
+ }
49
+ }
50
+ return steps.every((step) => step.ok) ? 0 : 1;
51
+ }
52
+ async function serve(flags, dataDir) {
53
+ const settings = relaySettings(flags, process.env);
54
+ if (!dataDir) {
55
+ throw new RelaySettingsError('set --data-dir (PAIRLOBBY_DATA_DIR) to where the rooms should live, such as a mounted volume');
56
+ }
57
+ const { DEFAULT_PORT, backupTarget, startServer } = await import('./index.js');
58
+ const writeLine = (line) => process.stdout.write(`${line}\n`);
59
+ const backup = settings.backup ? { target: backupTarget(settings.backup.location), intervalMs: settings.backup.intervalMs, keep: settings.backup.keep, restore: settings.backup.restore, log: writeLine } : undefined;
60
+ const host = settings.host ?? '127.0.0.1';
61
+ const running = await startServer({
62
+ version: version(),
63
+ host,
64
+ port: settings.port ?? DEFAULT_PORT,
65
+ dataFile: join(dataDir, 'rooms.sqlite'),
66
+ ...(settings.publicUrl ? { publicUrl: settings.publicUrl } : {}),
67
+ ...(settings.trustedProxyHeader ? { trustedProxyHeader: settings.trustedProxyHeader } : {}),
68
+ ...(settings.createToken ? { createToken: settings.createToken } : {}),
69
+ allowedHosts: settings.allowedHosts,
70
+ limits: settings.limits,
71
+ roomLimits: settings.roomLimits,
72
+ ...(settings.requestLog ? { requestLog: { mode: settings.requestLog, write: writeLine } } : {}),
73
+ ...(backup ? { backup } : {})
74
+ });
75
+ log({ level: 'info', event: 'start', version: version(), url: running.url, listening: `${running.host}:${running.port}`, dataFile: running.dataFile, createToken: Boolean(settings.createToken), backup: backup?.target.description ?? null, restore: running.restore?.outcome ?? null });
76
+ if (!settings.createToken && !['127.0.0.1', '::1', 'localhost'].includes(host)) {
77
+ log({ level: 'warn', event: 'open-creation', message: 'anyone who can reach this relay can create rooms; set PAIRLOBBY_CREATE_TOKEN to require a token' });
78
+ }
79
+ await new Promise((resolve) => {
80
+ const stop = (signal) => {
81
+ log({ level: 'info', event: 'stopping', signal });
82
+ void running.close().then(resolve);
83
+ };
84
+ process.once('SIGINT', () => stop('SIGINT'));
85
+ process.once('SIGTERM', () => stop('SIGTERM'));
86
+ });
87
+ log({ level: 'info', event: 'stop' });
88
+ return 0;
89
+ }
90
+ async function main(argv) {
91
+ const { values, positionals } = parseArgs({
92
+ args: argv,
93
+ allowPositionals: true,
94
+ options: {
95
+ help: { type: 'boolean', short: 'h' },
96
+ version: { type: 'boolean' },
97
+ json: { type: 'boolean' },
98
+ 'data-dir': { type: 'string' },
99
+ host: { type: 'string' },
100
+ port: { type: 'string' },
101
+ 'public-url': { type: 'string' },
102
+ 'trusted-proxy-header': { type: 'string' },
103
+ 'request-log': { type: 'string' },
104
+ backup: { type: 'string' },
105
+ 'no-restore': { type: 'boolean' },
106
+ },
107
+ });
108
+ if (values.help) {
109
+ process.stdout.write(HELP);
110
+ return 0;
111
+ }
112
+ if (values.version) {
113
+ process.stdout.write(`${version()}\n`);
114
+ return 0;
115
+ }
116
+ if (positionals[0] === 'check') {
117
+ return check(positionals[1], values.json === true);
118
+ }
119
+ if (positionals.length > 0) {
120
+ process.stderr.write(`pairlobby-relay: unknown command "${positionals[0]}"\n\n${HELP}`);
121
+ return 2;
122
+ }
123
+ const flags = {
124
+ ...(values.host !== undefined ? { host: values.host } : {}),
125
+ ...(values.port !== undefined ? { port: values.port } : {}),
126
+ ...(values['public-url'] !== undefined ? { publicUrl: values['public-url'] } : {}),
127
+ ...(values['trusted-proxy-header'] !== undefined ? { trustedProxyHeader: values['trusted-proxy-header'] } : {}),
128
+ ...(values['request-log'] !== undefined ? { requestLog: values['request-log'] } : {}),
129
+ ...(values.backup !== undefined ? { backup: values.backup } : {}),
130
+ ...(values['no-restore'] ? { noRestore: true } : {}),
131
+ };
132
+ return serve(flags, values['data-dir'] ?? (process.env['PAIRLOBBY_DATA_DIR']?.trim() || undefined));
133
+ }
134
+ main(process.argv.slice(2)).then((code) => {
135
+ process.exitCode = code;
136
+ }, (error) => {
137
+ const usage = error instanceof RelaySettingsError || error.code === 'ERR_PARSE_ARGS_UNKNOWN_OPTION';
138
+ log({ level: 'error', event: usage ? 'bad-settings' : 'failed', message: error instanceof Error ? error.message : String(error) });
139
+ process.exitCode = usage ? 2 : 1;
140
+ });
141
+ //# sourceMappingURL=relay-main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-main.js","sourceRoot":"","sources":["../src/relay-main.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /** `all` logs every request; `writes` skips successful reads, which clients repeat every second while they wait. */
2
+ export type RequestLogMode = 'all' | 'writes';
3
+ export declare const REQUEST_LOG_MODES: readonly RequestLogMode[];
4
+ export type RequestLogOptions = {
5
+ mode: RequestLogMode;
6
+ /** Receives each line; a service writes it to standard output for its log collector. */
7
+ write: (line: string) => void;
8
+ };
9
+ export type RequestLogLine = {
10
+ ts: string;
11
+ level: 'info' | 'warn';
12
+ event: 'request';
13
+ requestId: string;
14
+ method: string;
15
+ route: string;
16
+ status: number;
17
+ ms: number;
18
+ roomHash?: string;
19
+ };
20
+ type RequestLogEntry = {
21
+ requestId: string;
22
+ method: string;
23
+ path: string;
24
+ status: number;
25
+ ms: number;
26
+ at: Date;
27
+ };
28
+ export declare const REQUEST_ID_HEADER = "x-request-id";
29
+ /** The caller's request id when it is a short plain token, else a new one; a caller cannot inject text into the log. */
30
+ export declare function requestIdFor(offered: string | null): string;
31
+ /** The path with every identifier replaced, so lines group by route and carry no room, file or request id. */
32
+ export declare function routePattern(path: string): string;
33
+ /** The first eight hex digits of the room id's SHA-256: enough to follow one room through the log, not to find it. */
34
+ export declare function roomHashOf(path: string): string | undefined;
35
+ export declare function shouldLog(mode: RequestLogMode, method: string, status: number): boolean;
36
+ export declare function requestLogLine({ requestId, method, path, status, ms, at }: RequestLogEntry): string;
37
+ export {};
38
+ //# sourceMappingURL=request-log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-log.d.ts","sourceRoot":"","sources":["../src/request-log.ts"],"names":[],"mappings":"AAKA,oHAAoH;AACpH,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,QAAQ,CAAC;AAE9C,eAAO,MAAM,iBAAiB,EAAE,SAAS,cAAc,EAAsB,CAAC;AAE9E,MAAM,MAAM,iBAAiB,GAAG;IAC5B,IAAI,EAAE,cAAc,CAAC;IACrB,wFAAwF;IACxF,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,KAAK,EAAE,SAAS,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,KAAK,eAAe,GAAG;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,IAAI,CAAA;CAAC,CAAC;AAE/G,eAAO,MAAM,iBAAiB,iBAAiB,CAAC;AAIhD,wHAAwH;AACxH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAE3D;AAED,8GAA8G;AAC9G,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQjD;AAED,sHAAsH;AACtH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAM3D;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAEvF;AAED,wBAAgB,cAAc,CAAC,EAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAC,EAAE,eAAe,GAAG,MAAM,CAIjG"}
@@ -0,0 +1,37 @@
1
+ //! One JSON line per request, for a relay run as a service: what was asked, how it
2
+ //! ended and how long it took, never a body, a credential, an invite code or a name.
3
+ import { createHash, randomUUID } from 'node:crypto';
4
+ export const REQUEST_LOG_MODES = ['all', 'writes'];
5
+ export const REQUEST_ID_HEADER = 'x-request-id';
6
+ const IDENTIFIER = /^[a-z]{2}_[0-9A-Z]{20,32}$/;
7
+ /** The caller's request id when it is a short plain token, else a new one; a caller cannot inject text into the log. */
8
+ export function requestIdFor(offered) {
9
+ return offered && /^[A-Za-z0-9._-]{1,64}$/.test(offered) ? offered : randomUUID();
10
+ }
11
+ /** The path with every identifier replaced, so lines group by route and carry no room, file or request id. */
12
+ export function routePattern(path) {
13
+ const segments = path.split('/').filter(Boolean).map((segment, index, all) => {
14
+ if (index === 2 && all[0] === 'v1' && all[1] === 'rooms' && segment !== 'local') {
15
+ return ':room';
16
+ }
17
+ return IDENTIFIER.test(segment) ? ':id' : segment;
18
+ });
19
+ return `/${segments.join('/')}`;
20
+ }
21
+ /** The first eight hex digits of the room id's SHA-256: enough to follow one room through the log, not to find it. */
22
+ export function roomHashOf(path) {
23
+ const segments = path.split('/').filter(Boolean);
24
+ if (segments[0] !== 'v1' || segments[1] !== 'rooms' || !segments[2] || segments[2] === 'local') {
25
+ return undefined;
26
+ }
27
+ return createHash('sha256').update(segments[2]).digest('hex').slice(0, 8);
28
+ }
29
+ export function shouldLog(mode, method, status) {
30
+ return mode === 'all' || status >= 400 || !['GET', 'HEAD'].includes(method);
31
+ }
32
+ export function requestLogLine({ requestId, method, path, status, ms, at }) {
33
+ const roomHash = roomHashOf(path);
34
+ const line = { ts: at.toISOString(), level: status >= 500 ? 'warn' : 'info', event: 'request', requestId, method, route: routePattern(path), status, ms, ...(roomHash ? { roomHash } : {}) };
35
+ return JSON.stringify(line);
36
+ }
37
+ //# sourceMappingURL=request-log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-log.js","sourceRoot":"","sources":["../src/request-log.ts"],"names":[],"mappings":"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"}
package/dist/s3.d.ts ADDED
@@ -0,0 +1,37 @@
1
+ export type S3Credentials = {
2
+ accessKeyId: string;
3
+ secretAccessKey: string;
4
+ sessionToken?: string;
5
+ };
6
+ export type S3Environment = Record<string, string | undefined>;
7
+ export type S3ClientOptions = {
8
+ bucket: string;
9
+ region: string;
10
+ /** An S3-compatible endpoint such as `https://<account>.r2.cloudflarestorage.com`; addressed path-style. Absent means AWS. */
11
+ endpoint?: string;
12
+ /** Absent means the usual AWS chain: environment, then the ECS task role, then the EC2 instance role. */
13
+ credentials?: () => Promise<S3Credentials>;
14
+ fetch?: typeof fetch;
15
+ };
16
+ export type SignInput = {
17
+ method: string;
18
+ url: URL;
19
+ headers: Record<string, string>;
20
+ payloadHash: string;
21
+ credentials: S3Credentials;
22
+ region: string;
23
+ at: Date;
24
+ };
25
+ export type S3Client = {
26
+ putObject(key: string, body: Uint8Array): Promise<void>;
27
+ /** Null when the object does not exist. */
28
+ getObject(key: string): Promise<Uint8Array | null>;
29
+ listKeys(prefix: string): Promise<string[]>;
30
+ deleteObject(key: string): Promise<void>;
31
+ };
32
+ /** The request's headers plus `x-amz-date`, the token when there is one, and `authorization`. */
33
+ export declare function signRequest({ method, url, headers, payloadHash, credentials, region, at }: SignInput): Record<string, string>;
34
+ /** The default AWS credential chain, cached until five minutes before temporary credentials expire. */
35
+ export declare function awsCredentials(environment?: S3Environment, fetcher?: typeof fetch): () => Promise<S3Credentials>;
36
+ export declare function s3Client({ bucket, region, endpoint, credentials, fetch: fetcher }: S3ClientOptions): S3Client;
37
+ //# sourceMappingURL=s3.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3.d.ts","sourceRoot":"","sources":["../src/s3.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,aAAa,GAAG;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAC,CAAC;AAIlG,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAE/D,MAAM,MAAM,eAAe,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,8HAA8H;IAC9H,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yGAAyG;IACzG,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,aAAa,CAAC,CAAC;IAC3C,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,GAAG,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,IAAI,CAAA;CAAC,CAAC;AAE/J,MAAM,MAAM,QAAQ,GAAG;IACnB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,2CAA2C;IAC3C,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACnD,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5C,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5C,CAAC;AAiBF,iGAAiG;AACjG,wBAAgB,WAAW,CAAC,EAAC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,EAAC,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAa3H;AA+CD,uGAAuG;AACvG,wBAAgB,cAAc,CAAC,WAAW,GAAE,aAA2B,EAAE,OAAO,GAAE,OAAO,KAAa,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,CAQpI;AAOD,wBAAgB,QAAQ,CAAC,EAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,WAA8B,EAAE,KAAK,EAAE,OAAe,EAAC,EAAE,eAAe,GAAG,QAAQ,CAqDtI"}
package/dist/s3.js ADDED
@@ -0,0 +1,140 @@
1
+ //! The few S3 calls a relay backup needs, signed with AWS Signature Version 4 over
2
+ //! `fetch`, so the relay carries no SDK. Works with AWS S3 and with S3-compatible
3
+ //! stores such as Cloudflare R2 or MinIO through an explicit endpoint.
4
+ import { createHash, createHmac } from 'node:crypto';
5
+ import { readFileSync } from 'node:fs';
6
+ const EMPTY_HASH = createHash('sha256').update('').digest('hex');
7
+ function sha256Hex(data) {
8
+ return createHash('sha256').update(data).digest('hex');
9
+ }
10
+ function hmac(key, data) {
11
+ return createHmac('sha256', key).update(data).digest();
12
+ }
13
+ /** RFC 3986 encoding, which SigV4 requires and `encodeURIComponent` falls short of. */
14
+ function encodeStrict(value) {
15
+ return encodeURIComponent(value).replace(/[!'()*]/g, (character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`);
16
+ }
17
+ /** The request's headers plus `x-amz-date`, the token when there is one, and `authorization`. */
18
+ export function signRequest({ method, url, headers, payloadHash, credentials, region, at }) {
19
+ const amzDate = at.toISOString().replace(/[-:]/g, '').replace(/\.\d{3}/, '');
20
+ const day = amzDate.slice(0, 8);
21
+ const signed = { ...headers, host: url.host, 'x-amz-date': amzDate, 'x-amz-content-sha256': payloadHash, ...(credentials.sessionToken ? { 'x-amz-security-token': credentials.sessionToken } : {}) };
22
+ const names = Object.keys(signed).map((name) => name.toLowerCase()).sort();
23
+ const lower = Object.fromEntries(Object.entries(signed).map(([name, value]) => [name.toLowerCase(), value.trim().replace(/\s+/g, ' ')]));
24
+ const query = [...url.searchParams].map(([name, value]) => `${encodeStrict(name)}=${encodeStrict(value)}`).sort().join('&');
25
+ const canonical = [method, url.pathname.split('/').map((segment) => encodeStrict(decodeURIComponent(segment))).join('/'), query, names.map((name) => `${name}:${lower[name]}\n`).join(''), names.join(';'), payloadHash].join('\n');
26
+ const scope = `${day}/${region}/s3/aws4_request`;
27
+ const toSign = ['AWS4-HMAC-SHA256', amzDate, scope, sha256Hex(canonical)].join('\n');
28
+ const key = hmac(hmac(hmac(hmac(`AWS4${credentials.secretAccessKey}`, day), region), 's3'), 'aws4_request');
29
+ const signature = createHmac('sha256', key).update(toSign).digest('hex');
30
+ return { ...signed, authorization: `AWS4-HMAC-SHA256 Credential=${credentials.accessKeyId}/${scope}, SignedHeaders=${names.join(';')}, Signature=${signature}` };
31
+ }
32
+ function fromRoleBody(body) {
33
+ return { accessKeyId: body.AccessKeyId, secretAccessKey: body.SecretAccessKey, ...(body.Token ? { sessionToken: body.Token } : {}), ...(body.Expiration ? { expiresAt: Date.parse(body.Expiration) } : {}) };
34
+ }
35
+ async function fetchJson(fetcher, url, headers = {}) {
36
+ const response = await fetcher(url, { headers, signal: AbortSignal.timeout(2000) });
37
+ if (!response.ok) {
38
+ throw new Error(`${url} answered ${response.status}`);
39
+ }
40
+ return (await response.json());
41
+ }
42
+ async function loadCredentials(environment, fetcher) {
43
+ const accessKeyId = environment['AWS_ACCESS_KEY_ID'];
44
+ const secretAccessKey = environment['AWS_SECRET_ACCESS_KEY'];
45
+ if (accessKeyId && secretAccessKey) {
46
+ const sessionToken = environment['AWS_SESSION_TOKEN'];
47
+ return { accessKeyId, secretAccessKey, ...(sessionToken ? { sessionToken } : {}) };
48
+ }
49
+ const relative = environment['AWS_CONTAINER_CREDENTIALS_RELATIVE_URI'];
50
+ if (relative) {
51
+ return fromRoleBody(await fetchJson(fetcher, `http://169.254.170.2${relative}`));
52
+ }
53
+ const full = environment['AWS_CONTAINER_CREDENTIALS_FULL_URI'];
54
+ if (full) {
55
+ const tokenFile = environment['AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE'];
56
+ const token = tokenFile ? readFileSync(tokenFile, 'utf8').trim() : environment['AWS_CONTAINER_AUTHORIZATION_TOKEN'];
57
+ return fromRoleBody(await fetchJson(fetcher, full, token ? { authorization: token } : {}));
58
+ }
59
+ // EC2 instance metadata, version 2: a session token first, then the role's credentials.
60
+ const tokenResponse = await fetcher('http://169.254.169.254/latest/api/token', { method: 'PUT', headers: { 'x-aws-ec2-metadata-token-ttl-seconds': '21600' }, signal: AbortSignal.timeout(1000) }).catch(() => null);
61
+ if (!tokenResponse?.ok) {
62
+ throw new Error('no AWS credentials: set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, or run with an ECS task role or an EC2 instance role');
63
+ }
64
+ const metadataToken = await tokenResponse.text();
65
+ const roles = await fetcher('http://169.254.169.254/latest/meta-data/iam/security-credentials/', { headers: { 'x-aws-ec2-metadata-token': metadataToken }, signal: AbortSignal.timeout(1000) });
66
+ const role = (await roles.text()).split('\n')[0]?.trim();
67
+ if (!roles.ok || !role) {
68
+ throw new Error('this EC2 instance has no IAM role to back up with');
69
+ }
70
+ return fromRoleBody(await fetchJson(fetcher, `http://169.254.169.254/latest/meta-data/iam/security-credentials/${role}`, { 'x-aws-ec2-metadata-token': metadataToken }));
71
+ }
72
+ /** The default AWS credential chain, cached until five minutes before temporary credentials expire. */
73
+ export function awsCredentials(environment = process.env, fetcher = fetch) {
74
+ let cached = null;
75
+ return async () => {
76
+ if (!cached || (cached.expiresAt !== undefined && cached.expiresAt - Date.now() < 5 * 60_000)) {
77
+ cached = await loadCredentials(environment, fetcher);
78
+ }
79
+ return cached;
80
+ };
81
+ }
82
+ function xmlValues(xml, tag) {
83
+ const unescape = (value) => value.replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&amp;/g, '&');
84
+ return [...xml.matchAll(new RegExp(`<${tag}>([^<]*)</${tag}>`, 'g'))].map((match) => unescape(match[1] ?? ''));
85
+ }
86
+ export function s3Client({ bucket, region, endpoint, credentials = awsCredentials(), fetch: fetcher = fetch }) {
87
+ const base = endpoint ? `${endpoint.replace(/\/+$/, '')}/${bucket}` : `https://${bucket}.s3.${region}.amazonaws.com`;
88
+ const objectUrl = (key) => new URL(`${base}/${key.split('/').map(encodeStrict).join('/')}`);
89
+ const send = async (method, url, body) => {
90
+ const headers = signRequest({ method, url, headers: {}, payloadHash: body ? sha256Hex(body) : EMPTY_HASH, credentials: await credentials(), region, at: new Date() });
91
+ delete headers['host'];
92
+ return fetcher(url, { method, headers, ...(body ? { body } : {}) });
93
+ };
94
+ const failed = async (response, what) => new Error(`S3 ${what} answered ${response.status}: ${xmlValues(await response.text(), 'Code')[0] ?? response.statusText}`);
95
+ return {
96
+ async putObject(key, body) {
97
+ const response = await send('PUT', objectUrl(key), body);
98
+ if (!response.ok) {
99
+ throw await failed(response, `upload of ${key}`);
100
+ }
101
+ },
102
+ async getObject(key) {
103
+ const response = await send('GET', objectUrl(key));
104
+ if (response.status === 404) {
105
+ return null;
106
+ }
107
+ if (!response.ok) {
108
+ throw await failed(response, `download of ${key}`);
109
+ }
110
+ return new Uint8Array(await response.arrayBuffer());
111
+ },
112
+ async listKeys(prefix) {
113
+ const keys = [];
114
+ let continuation;
115
+ do {
116
+ const url = new URL(`${base}/`);
117
+ url.searchParams.set('list-type', '2');
118
+ url.searchParams.set('prefix', prefix);
119
+ if (continuation) {
120
+ url.searchParams.set('continuation-token', continuation);
121
+ }
122
+ const response = await send('GET', url);
123
+ if (!response.ok) {
124
+ throw await failed(response, `listing of ${bucket}/${prefix}`);
125
+ }
126
+ const xml = await response.text();
127
+ keys.push(...xmlValues(xml, 'Key'));
128
+ continuation = xmlValues(xml, 'IsTruncated')[0] === 'true' ? xmlValues(xml, 'NextContinuationToken')[0] : undefined;
129
+ } while (continuation);
130
+ return keys;
131
+ },
132
+ async deleteObject(key) {
133
+ const response = await send('DELETE', objectUrl(key));
134
+ if (!response.ok && response.status !== 404) {
135
+ throw await failed(response, `delete of ${key}`);
136
+ }
137
+ }
138
+ };
139
+ }
140
+ //# sourceMappingURL=s3.js.map
package/dist/s3.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3.js","sourceRoot":"","sources":["../src/s3.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare const SCHEMA_VERSION = 1;
2
+ export declare const SCHEMA = "\nCREATE TABLE IF NOT EXISTS meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS rooms (\n room_id TEXT PRIMARY KEY,\n earliest_seq INTEGER NOT NULL DEFAULT 1,\n body TEXT NOT NULL\n) STRICT;\nCREATE INDEX IF NOT EXISTS rooms_by_auto_close ON rooms (json_extract(body, '$.autoCloseAt')) WHERE json_extract(body, '$.lifecycle') = 'open' AND json_extract(body, '$.autoCloseAt') IS NOT NULL;\n\nCREATE TABLE IF NOT EXISTS participants (\n participant_id TEXT PRIMARY KEY,\n room_id TEXT NOT NULL,\n credential_hash TEXT NOT NULL,\n body TEXT NOT NULL\n) STRICT;\nCREATE INDEX IF NOT EXISTS participants_by_room ON participants (room_id);\nCREATE UNIQUE INDEX IF NOT EXISTS participants_by_credential ON participants (room_id, credential_hash);\n\nCREATE TABLE IF NOT EXISTS events (\n room_id TEXT NOT NULL,\n seq INTEGER NOT NULL,\n body TEXT NOT NULL,\n PRIMARY KEY (room_id, seq)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS idempotency (\n room_id TEXT NOT NULL,\n key TEXT NOT NULL,\n request_digest TEXT NOT NULL,\n seq INTEGER NOT NULL,\n PRIMARY KEY (room_id, key)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS handovers (\n handover_id TEXT PRIMARY KEY,\n room_id TEXT NOT NULL,\n body TEXT NOT NULL\n) STRICT;\nCREATE INDEX IF NOT EXISTS handovers_by_room ON handovers (room_id);\n\nCREATE TABLE IF NOT EXISTS controls (\n room_id TEXT NOT NULL,\n target_participant_id TEXT NOT NULL,\n body TEXT NOT NULL,\n PRIMARY KEY (room_id, target_participant_id)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS invites (\n digest TEXT PRIMARY KEY,\n room_id TEXT NOT NULL,\n state TEXT NOT NULL,\n bound_attempt_id TEXT,\n body TEXT NOT NULL\n) STRICT;\nCREATE INDEX IF NOT EXISTS invites_by_room ON invites (room_id);\n\nCREATE TABLE IF NOT EXISTS message_requests (\n event_id TEXT PRIMARY KEY,\n room_id TEXT NOT NULL,\n seq INTEGER NOT NULL,\n received_at INTEGER,\n response_event_id TEXT,\n requires_reply INTEGER NOT NULL,\n body TEXT NOT NULL\n) STRICT;\nCREATE INDEX IF NOT EXISTS message_requests_pending ON message_requests(room_id,seq) WHERE requires_reply=1 AND response_event_id IS NULL;\nCREATE INDEX IF NOT EXISTS message_requests_group ON message_requests(room_id,json_extract(body,'$.conversationId'));\n\nCREATE TABLE IF NOT EXISTS files (\n file_id TEXT PRIMARY KEY,\n room_id TEXT NOT NULL,\n size INTEGER NOT NULL,\n created_at INTEGER NOT NULL,\n event_id TEXT,\n body TEXT NOT NULL\n) STRICT;\nCREATE INDEX IF NOT EXISTS files_by_room ON files (room_id, created_at);\n\nCREATE TABLE IF NOT EXISTS file_chunks (\n file_id TEXT NOT NULL,\n idx INTEGER NOT NULL,\n bytes BLOB NOT NULL,\n PRIMARY KEY (file_id, idx)\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS events_message_id ON events(room_id,json_extract(body,'$.eventId'));\nCREATE INDEX IF NOT EXISTS events_reply_to ON events(room_id,json_extract(body,'$.replyTo')) WHERE json_extract(body,'$.type')='message';\nCREATE INDEX IF NOT EXISTS events_receipt_to ON events(room_id,json_extract(body,'$.payload.eventId')) WHERE json_extract(body,'$.type')='message.received';\n";
3
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC,eAAO,MAAM,MAAM,m0GA4FlB,CAAC"}