/** * Opaque key-handle registry: TTL, optional max uses, revocation (no key material). */ /** @typedef {{ id: string, purpose: string, algorithm: string, atMs: number, expiresAtMs: number | null, maxUses: number, uses: number, scopes: string[], revoked: boolean }} BareOsSecretHandleRow */ /** @type {Map} */ const handles = new Map() let seq = 0 const MAX_HANDLES = 4096 /** * @param {{ purpose?: string, algorithm?: string, ttlMs?: number | null, maxUses?: number, scopes?: string[] }} opts */ export function bareOsSecretHandleAcquire(opts = {}) { if (handles.size >= MAX_HANDLES) { for (const [id, h] of handles) { if (h.revoked || bareOsSecretHandleIsExpired(h)) { handles.delete(id) break } } } const purpose = String(opts.purpose || 'default').trim().slice(0, 64) || 'default' const algorithm = String(opts.algorithm || 'opaque').trim().slice(0, 32) || 'opaque' const ttlRaw = opts.ttlMs != null ? Number.parseInt(String(opts.ttlMs), 10) : Number.NaN const ttlMs = Number.isFinite(ttlRaw) && ttlRaw > 0 ? Math.min(ttlRaw, 86400000) : null const maxUses = opts.maxUses != null && Number.isFinite(Number(opts.maxUses)) && Number(opts.maxUses) > 0 ? Math.min(1e6, Math.floor(Number(opts.maxUses))) : 1e9 const scopes = Array.isArray(opts.scopes) ? opts.scopes .map((s) => String(s).trim().slice(0, 48)) .filter(Boolean) .slice(0, 8) : [] const atMs = Date.now() const id = `kh_${++seq}_${atMs.toString(36)}` const row = { id, purpose, algorithm, atMs, expiresAtMs: ttlMs != null ? atMs + ttlMs : null, maxUses, uses: 0, scopes: scopes.length ? scopes : ['default'], revoked: false } handles.set(id, row) return { handle: id, algorithm, atMs, ttlMs, scopes: row.scopes, zeroizeAfterMs: ttlMs } } /** @param {BareOsSecretHandleRow} h */ function bareOsSecretHandleIsExpired(h) { if (h.revoked) return true if (h.expiresAtMs != null && Date.now() > h.expiresAtMs) return true if (h.uses >= h.maxUses) return true return false } /** * @param {string} id * @returns {boolean} */ export function bareOsSecretHandleTouch(id) { const h = handles.get(String(id || '')) if (!h || bareOsSecretHandleIsExpired(h)) return false h.uses++ if (h.uses >= h.maxUses) h.revoked = true return !h.revoked } /** * @param {string} id */ export function bareOsSecretHandleRelease(id) { const h = handles.get(String(id || '')) if (h) h.revoked = true handles.delete(String(id || '')) } export function bareOsSecretHandlePruneExpired() { const now = Date.now() for (const [id, h] of handles) { if (h.revoked || (h.expiresAtMs != null && now > h.expiresAtMs) || h.uses >= h.maxUses) handles.delete(id) } } /** Non-secret snapshot for `/proc` / posture. */ export function bareOsSecretHandleSnapshot() { bareOsSecretHandlePruneExpired() let active = 0 for (const h of handles.values()) { if (!bareOsSecretHandleIsExpired(h)) active++ } return { schema: 1, totalRegistered: handles.size, activeHandles: active, cap: MAX_HANDLES, atMs: Date.now() } }