relay
@noy-db/in-relay·pnpm add @noy-db/hub @noy-db/in-relay@nextServe a vault to LAN or remote clients over any transport, with the dangerous store methods absent by construction.
What it does
createRelayHandler({ store }) returns (frame) => Promise<RelayResult> — it takes a
decoded request frame and returns a decoded result, so you serve it over HTTP, a
WebSocket, or QUIC without this package knowing which. The method vocabulary is fixed:
RELAY_METHODS // ['get', 'put', 'delete', 'list', 'loadAll', 'ping', 'listSince']
Its store is typed as NoydbRelayStore from
@noy-db/hub/to — a narrowed store profile, not the full NoydbStore.
Two exclusions, two different reasons
| absent method | why |
|---|---|
saveAll | whole-vault replace — a relay that can be asked to overwrite a vault wholesale is a rollback superweapon aimed at the hosts trusting it |
listVaults | enumeration is an existence leak — a relay serves vaults the caller already names, and never answers "what else is here" |
The exclusions are structural. RELAY_METHODS is an allowlist by construction: those
two names are absent because they are absent from NoydbRelayStore, not because they were
filtered out. Adding either name to the list would not compile a call.
in-rest with a narrower allow set.in-rest types its store as a
full NoydbStore and gates dispatch with a runtime Set — correct for in-rest, whose job
is to serve a whole store. Building a relay that way means handing the handler an object that
carriessaveAll and trusting a Set not to call it: structural absence downgraded to a
runtime allowlist, which is the one property a relay exists to have.Unknown method, never forbidden
An excluded method is refused as 400 unknown, never 403 forbidden. A 403 would
confirm the method exists and is merely disallowed here, which names the excluded surface to
anyone probing.
Measured against the published 0.7.0-pre.16 — an excluded method and a name that never
existed are indistinguishable:
get → ok
saveAll → status 400 UnknownRelayMethodError
listVaults → status 400 UnknownRelayMethodError
totallyMadeUp → status 400 UnknownRelayMethodError
That is the security posture, not a missing case. @doi-db/daemon reached the same
conclusion independently in its own native implementation.
Usage
import { createRelayHandler } from '@noy-db/in-relay'
import { toFile } from '@noy-db/to-file'
const handle = createRelayHandler({ store: toFile({ dir: './vaults' }) })
// Over any transport you like — here, a bare fetch handler.
export default async function (req: Request): Promise<Response> {
const frame = await req.json()
const result = await handle(frame)
return Response.json(result, { status: result.ok ? 200 : result.status })
}
RelayResult is a discriminated union: { ok: true, id, value }, or
{ ok: false, id, status: 400 | 500, error: { name, message } } — a transport maps status
directly. Since 0.10.0-pre.2 a store failure reaches the client as the message 'store error':
the original message is kept only for a 501 and for ConflictError, so an internal error detail
no longer leaks to the caller.
Install note
Through 0.7.0-pre.16 this package's latest dist-tag pointed at a pre-release, so a plain
npm i @noy-db/in-relay silently moved hub onto the pre line
(noy-db#1293). That ended when stables shipped: latest
is a stable release, and @noy-db/in-relay@next is the pre line — install the one that matches
your hub.
See also
- in-* family
- rest — the full-store HTTP handler this deliberately is not
- stores — the store contract
NoydbRelayStorenarrows