Package
@epheme/core
Common middleware and utilities used across the Epheme stack: logging, device auth, plugin hosting, and license middleware.
Overview
- Design focuses on privacy (redaction of sensitive fields) and clear, testable primitives.
- Exports small, well-documented helper modules that are composed by hosts.
deviceRegistry.js — Device JWT + middleware
'use strict';
/**
* @epheme/core/deviceRegistry
*
* Portable device-authentication primitives for any BafGo-compatible server.
* Wraps the JWT issuance/verification logic from hub/backend/index.js and
* exposes Express-compatible middleware factories.
*/
const crypto = require('crypto');
const jwt = require('jsonwebtoken');
// ── Rate-limit state for failed admin secret checks ──────────────────────────
const _adminFailMap = new Map(); // ip → { count, resetAt }
const ADMIN_WINDOW_MS = 60_000; // 1 minute sliding window
const ADMIN_MAX_FAILURES = 10;
function _adminCheckRate(ip) {
const now = Date.now();
const w = _adminFailMap.get(ip);
if (!w || now > w.resetAt) return true;
return w.count < ADMIN_MAX_FAILURES;
}
function _adminRecordFail(ip) {
const now = Date.now();
const w = _adminFailMap.get(ip);
if (!w || now > w.resetAt) {
_adminFailMap.set(ip, { count: 1, resetAt: now + ADMIN_WINDOW_MS });
} else {
w.count++;
}
}
// ─────────────────────────────────────────────────────────────────────────────
function createDeviceRegistry({ deviceJwtSecret, deviceJwtTtl = 3600 } = {}) {
function issueDeviceJWT(device) {
if (!deviceJwtSecret) throw new Error('DEVICE_JWT_SECRET not configured');
const payload = {
device_id: device.id,
tenant: device.tenant,
role: device.role,
type: 'device_access',
};
if (device.cert_fingerprint) payload.certFingerprint = device.cert_fingerprint;
return jwt.sign(payload, deviceJwtSecret, { expiresIn: deviceJwtTtl });
}
function verifyDeviceJWT(token) {
if (!deviceJwtSecret || !token) return null;
try {
const payload = jwt.verify(token, deviceJwtSecret);
if (payload.type !== 'device_access') return null;
return payload;
} catch {
return null;
}
}
function requireDevice({ requiredRole } = {}) {
return function deviceAuthMiddleware(req, res, next) {
const authHeader = req.get('Authorization') || '';
const bearer = authHeader.startsWith('Bearer ') ? authHeader.slice(7) : null;
const payload = verifyDeviceJWT(bearer);
if (!payload) {
return res.status(401).json({ error: 'Device authentication required' });
}
if (requiredRole && payload.role !== requiredRole) {
return res.status(403).json({ error: `Role '${requiredRole}' required` });
}
req.device = payload;
next();
};
}
function requireAdmin({ envKey = 'DEVICE_ADMIN_SECRET' } = {}) {
return function adminAuthMiddleware(req, res, next) {
const secret = process.env[envKey];
if (!secret) {
return res.status(503).json({ error: `Device admin not configured (${envKey} missing)` });
}
const ip = req.ip || req.socket?.remoteAddress || 'unknown';
if (!_adminCheckRate(ip)) {
return res.status(429).json({ error: 'Too many failed attempts. Try again later.' });
}
const provided = req.get('x-device-admin-secret') || '';
const hmacKey = crypto.createHash('sha256').update('admin-secret-comparison').digest();
const a = crypto.createHmac('sha256', hmacKey).update(secret).digest();
const b = crypto.createHmac('sha256', hmacKey).update(provided).digest();
if (!crypto.timingSafeEqual(a, b)) {
_adminRecordFail(ip);
return res.status(403).json({ error: 'Invalid or missing X-Device-Admin-Secret' });
}
next();
};
}
return { issueDeviceJWT, verifyDeviceJWT, requireDevice, requireAdmin };
}
module.exports = { createDeviceRegistry };
pluginHost.js — plugin loading and context
/**
* createPluginHost — BafGo backend plugin host.
*
* Discovers, validates, and mounts backend plugins onto an Express app.
*/
'use strict';
const express = require('express');
const semver = require('semver');
const { makeFeatureLicenseMiddleware } = require('./licenseMiddleware');
const { recordHit } = require('./metrics');
const { createPluginDb } = require('./db/pluginDb');
const CORE_VERSION = require('./package.json').version;
const PLUGIN_ID_RE = /^[a-z0-9][a-z0-9-]{1,31}$/;
async function createPluginHost(app, options = {}) {
const {
redis = null,
licensePublicKeyPem = null,
tenantLicenseClaims = null,
eventBus = null,
db: dbOptions = null,
plugins = [],
} = options;
const loadedPlugins = [];
for (const pluginExport of plugins) {
const plugin = pluginExport?.default ?? pluginExport;
if (!plugin || typeof plugin.register !== 'function') {
throw new Error(
`BafGo plugin host: a plugin entry does not implement EphemeBackendPlugin ` +
`(missing register() function). Got: ${JSON.stringify(Object.keys(plugin || {}))}`
);
}
const pluginId = String(plugin.id || '');
if (!PLUGIN_ID_RE.test(pluginId)) {
throw new Error(`BafGo plugin host: invalid pluginId "${pluginId}".`);
}
const manifest = plugin.manifest ?? null;
if (manifest?.requiredCoreVersion) {
if (!semver.satisfies(CORE_VERSION, manifest.requiredCoreVersion)) {
throw new Error(
`BafGo plugin host: plugin "${pluginId}" requires @epheme/core@` +
`${manifest.requiredCoreVersion} but installed version is ${CORE_VERSION}`
);
}
}
const ctx = buildPluginContext({ pluginId, redis, licensePublicKeyPem, tenantLicenseClaims, eventBus, dbOptions, declaredHubEvents: manifest?.hubEvents ?? [] });
await plugin.register(ctx);
app.use(`/plugins/${pluginId}`, ctx.router);
loadedPlugins.push({ pluginId, plugin, ctx });
ctx.logger.info(`loaded — routes mounted at /plugins/${pluginId}`);
}
return {
async shutdown() {
for (const { pluginId, plugin, ctx } of loadedPlugins) {
try {
await plugin.onShutdown?.();
await ctx._db?.close?.();
} catch (err) {
ctx.logger.error({ err }, 'error during shutdown');
}
}
},
};
}
module.exports = { createPluginHost };
logger.js — Privacy-safe structured logging
'use strict';
const pino = require('pino');
const pinoHttp = require('pino-http');
const { randomUUID } = require('node:crypto');
const REDACT_PATHS = [
'deviceId', 'deviceJwt', 'userId', 'jwt', 'token', 'pat', 'patId', 'sessionId',
'roomId', 'shareId', 'inviteToken', 'ip', 'remoteAddress', 'req.headers', 'req.remoteAddress', 'req.url', 'req.id', 'req.params', 'req.query', 'res.headers', 'authorization', 'cookie', 'password', 'secret', 'apiKey', 'body',
];
function serializeErr(err) {
if (!err || typeof err !== 'object') return err;
const out = {};
if (err.name || err.constructor?.name) out.type = err.name ?? err.constructor.name;
if (err.message !== undefined) out.message = String(err.message);
if (err.stack !== undefined) out.stack = String(err.stack);
if (err.code !== undefined) out.code = err.code;
if (err.statusCode !== undefined) out.statusCode = err.statusCode;
return out;
}
function safeRoute(req) {
if (req.route && req.route.path) return req.route.path;
return (req.url || '/')
.split('?')[0]
.replace(/\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, '/:id')
.replace(/\/[0-9a-f]{16,}/gi, '/:id')
.replace(/\/\d{4,}/g, '/:id');
}
function createLogger({ service, component, tenant, destination } = {}) {
const env = process.env.NODE_ENV || 'development';
const level = process.env.LOG_LEVEL || (env === 'production' ? 'info' : 'debug');
const usePretty = !destination
&& process.env.LOG_PRETTY !== 'false'
&& (process.env.LOG_PRETTY === 'true' || (process.stdout.isTTY && env !== 'test'));
const base = { environment: env };
const svc = service || process.env.LOG_SERVICE;
if (svc) base.service = svc;
if (component) base.component = component;
const ten = tenant || process.env.TENANT_SLUG;
if (ten) base.tenant = ten;
const ns = process.env.K8S_NAMESPACE;
if (ns) base.namespace = ns;
const opts = {
level,
redact: { paths: REDACT_PATHS, remove: true },
serializers: { err: serializeErr },
base,
};
if (usePretty) {
return pino(opts, pino.transport({ target: 'pino-pretty', options: { colorize: true, translateTime: 'SYS:standard', ignore: 'pid,hostname' } }));
}
return destination ? pino(opts, destination) : pino(opts);
}
module.exports = { createLogger, requestLogger: pinoHttp, REDACT_PATHS, _safeRoute: safeRoute };
licenseMiddleware.js — License verification middleware
const jwt = require('jsonwebtoken');
function extractBearerToken(authorizationHeader) {
const header = String(authorizationHeader || '').trim();
if (!header.startsWith('Bearer ')) return null;
const token = header.slice(7).trim();
return token || null;
}
function formatMissingFeatureError(requiredFeatures) {
if (requiredFeatures.length === 1) return `${requiredFeatures[0]} feature required`;
return `Missing required license features: ${requiredFeatures.join(', ')}`;
}
function makeLicensePublicKeyHandler(options = {}) {
const {
getPublicKeyPem,
missingMessage = 'License public key not configured',
} = options;
if (typeof getPublicKeyPem !== 'function') {
throw new Error('makeLicensePublicKeyHandler requires getPublicKeyPem function');
}
return function licensePublicKeyHandler(_req, res) {
const pem = getPublicKeyPem();
if (!pem) {
return res.status(503).json({ error: missingMessage });
}
return res.type('text/plain').send(pem);
};
}
/**
* Build an Express middleware that verifies an RS256 license JWT and checks
* required license fields/features.
*/
function makeFeatureLicenseMiddleware(options = {}) {
const {
getPublicKeyPem,
requiredLicense = 'premium',
requiredFeatures = [],
attachProperty = 'licensePayload',
precheck,
validatePayload,
logPrefix = 'license',
} = options;
if (typeof getPublicKeyPem !== 'function') {
throw new Error('makeFeatureLicenseMiddleware requires getPublicKeyPem function');
}
return async function featureLicenseMiddleware(req, res, next) {
const precheckError = typeof precheck === 'function' ? precheck(req) : null;
if (precheckError) {
return res.status(503).json({ error: precheckError });
}
const publicKeyPem = getPublicKeyPem(req);
if (!publicKeyPem) {
return res.status(503).json({ error: 'License verification not configured' });
}
const token = extractBearerToken(req.headers?.authorization);
if (!token) {
return res.status(401).json({ error: 'Missing Authorization header' });
}
try {
const verified = jwt.verify(token, publicKeyPem, { algorithms: ['RS256'] });
const payload = (verified && typeof verified === 'object') ? verified : null;
if (!payload) {
return res.status(401).json({ error: 'Invalid or expired license' });
}
if (requiredLicense && payload.lic !== requiredLicense) {
return res.status(403).json({ error: 'Premium license required' });
}
if (requiredFeatures.length > 0) {
const features = Array.isArray(payload.features) ? payload.features : [];
const missing = requiredFeatures.filter(f => !features.includes(f));
if (missing.length > 0) {
return res.status(403).json({ error: formatMissingFeatureError(requiredFeatures) });
}
}
if (typeof validatePayload === 'function') {
const validationError = validatePayload(req, payload);
if (validationError) {
const status = Number(validationError.status) || 403;
const error = validationError.error || 'License validation failed';
return res.status(status).json({ error });
}
}
req[attachProperty] = payload;
return next();
} catch (err) {
console.warn(`[${logPrefix}] License verification failed:`, err && err.message ? err.message : err);
return res.status(401).json({ error: 'Invalid or expired license' });
}
};
}
module.exports = {
extractBearerToken,
makeFeatureLicenseMiddleware,
makeLicensePublicKeyHandler,
};
db/pluginDb.js — Plugin database adapters
/**
* EphemeDb — internal interface implemented by both the SQLite and Postgres adapters.
* This mirrors PluginDb from @epheme/plugin-sdk exactly, plus the internal
* lifecycle methods the plugin host needs.
*/
'use strict';
function createSqliteDb({ pluginId, file }) {
const Database = require('better-sqlite3');
const db = new Database(file);
db.pragma('journal_mode = WAL');
db.pragma('foreign_keys = ON');
db.exec(`
CREATE TABLE IF NOT EXISTS _epheme_migrations (
idx INTEGER PRIMARY KEY,
applied INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
)
`);
async function query(sql, params = []) {
const stmt = db.prepare(sql);
return stmt.all(...params);
}
async function run(sql, params = []) {
const stmt = db.prepare(sql);
stmt.run(...params);
}
async function transaction(fn) {
db.exec('BEGIN');
try {
const result = await fn(instance);
db.exec('COMMIT');
return result;
} catch (err) {
db.exec('ROLLBACK');
throw err;
}
}
async function migrate(steps) {
const applied = new Set(
db.prepare('SELECT idx FROM _epheme_migrations').all().map(r => r.idx)
);
for (let i = 0; i < steps.length; i++) {
if (applied.has(i)) continue;
db.exec(steps[i]);
db.prepare('INSERT INTO _epheme_migrations (idx) VALUES (?)').run(i);
}
}
function close() { db.close(); }
const instance = { query, run, transaction, migrate, close };
return instance;
}
function createPostgresDb({ pluginId, url }) {
const { Pool } = require('pg');
const pool = new Pool({ connectionString: url });
let migrationTableReady = false;
async function ensureMigrationTable(client) {
if (migrationTableReady) return;
await client.query(`
CREATE TABLE IF NOT EXISTS _epheme_migrations (
plugin_id TEXT NOT NULL,
idx INTEGER NOT NULL,
applied TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (plugin_id, idx)
)
`);
migrationTableReady = true;
}
function translatePlaceholders(sql) {
let i = 0;
return sql.replace(/\?/g, () => `$${++i}`);
}
async function query(sql, params = []) { const { rows } = await pool.query(translatePlaceholders(sql), params); return rows; }
async function run(sql, params = []) { await pool.query(translatePlaceholders(sql), params); }
async function transaction(fn) {
const client = await pool.connect();
try {
await client.query('BEGIN');
const txDb = {
query: (s, p = []) => client.query(translatePlaceholders(s), p).then(r => r.rows),
run: (s, p = []) => client.query(translatePlaceholders(s), p).then(() => undefined),
transaction: (innerFn) => innerFn(txDb),
migrate: () => Promise.reject(new Error('migrate() cannot be called inside a transaction')),
close: () => {},
};
const result = await fn(txDb);
await client.query('COMMIT');
return result;
} catch (err) {
await client.query('ROLLBACK');
throw err;
} finally { client.release(); }
}
async function migrate(steps) {
const client = await pool.connect();
try {
await client.query('BEGIN');
await ensureMigrationTable(client);
const { rows: applied } = await client.query('SELECT idx FROM _epheme_migrations WHERE plugin_id = $1', [pluginId]);
const appliedSet = new Set(applied.map(r => r.idx));
for (let i = 0; i < steps.length; i++) {
if (appliedSet.has(i)) continue;
await client.query(translatePlaceholders(steps[i]));
await client.query('INSERT INTO _epheme_migrations (plugin_id, idx) VALUES ($1, $2)', [pluginId, i]);
}
await client.query('COMMIT');
} catch (err) { await client.query('ROLLBACK'); throw err; } finally { client.release(); }
}
async function close() { await pool.end(); }
return { query, run, transaction, migrate, close };
}
function createPluginDb({ dialect, file, url, pluginId }) {
if (dialect === 'sqlite') {
if (!file) throw new Error(`createPluginDb: 'file' is required for sqlite dialect (plugin: ${pluginId})`);
return createSqliteDb({ pluginId, file });
}
if (dialect === 'postgres') {
if (!url) throw new Error(`createPluginDb: 'url' is required for postgres dialect (plugin: ${pluginId})`);
return createPostgresDb({ pluginId, url });
}
throw new Error(`createPluginDb: unknown dialect '${dialect}' (plugin: ${pluginId})`);
}
module.exports = { createPluginDb };
index.js — Package exports
// Main exports for @epheme/core
module.exports = {
createDeviceRegistry: require('./deviceRegistry').createDeviceRegistry,
createPluginHost: require('./pluginHost').createPluginHost,
createLogger: require('./logger').createLogger,
requestLogger: require('./logger').requestLogger,
makeFeatureLicenseMiddleware: require('./licenseMiddleware').makeFeatureLicenseMiddleware,
makeLicensePublicKeyHandler: require('./licenseMiddleware').makeLicensePublicKeyHandler,
createPluginDb: require('./db/pluginDb').createPluginDb,
};
browser/quickConnectClient.ts — Quick Connect browser client
export interface QuickConnectInitiateResponse {
code: string;
ttl: number;
}
export async function initiateQuickConnect(baseUrl: string): Promise {
const url = new URL('/qc/initiate', baseUrl).toString();
const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' } });
if (!res.ok) throw new Error(`initiateQuickConnect failed: ${res.status}`);
return res.json();
}
export async function joinQuickConnect(baseUrl: string, code: string): Promise<{ id: string }> {
const url = new URL('/qc/join', baseUrl).toString();
const res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code }),
});
if (!res.ok) {
const err = await res.json().catch(() => ({ error: res.statusText }));
throw new Error(`joinQuickConnect failed: ${JSON.stringify(err)}`);
}
return res.json();
}
browser/deviceSession.ts — Device session helper
export class DeviceSession {
private storageKey = 'epheme:deviceToken';
constructor(private storage: Storage = localStorage) {}
setToken(token: string) {
this.storage.setItem(this.storageKey, token);
}
getToken(): string | null {
return this.storage.getItem(this.storageKey);
}
clear() {
this.storage.removeItem(this.storageKey);
}
isAuthenticated(): boolean {
return !!this.getToken();
}
async fetchWithAuth(input: RequestInfo, init?: RequestInit) {
const token = this.getToken();
const headers = new Headers(init && init.headers ? (init.headers as HeadersInit) : undefined);
if (token) headers.set('Authorization', `Bearer ${token}`);
const res = await fetch(input, { ...init, headers });
return res;
}
}