Updates
Release rolling / release (push) Successful in 9m23s

This commit is contained in:
Raven Scott
2026-07-14 20:27:41 -04:00
parent cb0b59f5c3
commit d822152c7e
4 changed files with 322 additions and 47 deletions
+77 -3
View File
@@ -57,6 +57,12 @@ export async function getAutopass() {
store = new Corestore(path.join(dir, 'store'))
pass = new Autopass(store)
await pass.ready()
// Restore invite meta so list/delete work after process restart
try {
await restoreLastInviteMeta(pass)
} catch (err) {
log.debug('invite meta restore skipped', { error: err.message })
}
log.info('AutoPass vault ready', { dir })
return pass
})()
@@ -105,9 +111,10 @@ export async function createConnectionInvite(opts = {}) {
await vault.add(PKG_KEY, packageValue)
// AutoPass single active invite — readOnly so invitees cannot rewrite the vault
const readOnly = role === Roles.viewer
const invite = await vault.createInvite({ readOnly })
// Always readOnly: invitees only need to read peardock:pkg (public key + capability).
// Writable invites hang in autopass@3 when the pairer awaits deleteInvite / writable.
// Role elevation is entirely via the HMAC capability, not Autopass write rights.
const invite = await vault.createInvite({ readOnly: true })
lastInviteMeta = {
invite,
@@ -194,4 +201,71 @@ export function isAutopassReady() {
return Boolean(pass)
}
/**
* Best-effort warm at server boot so BlindPairing is listening before clients redeem.
* Safe to call multiple times; failures are non-fatal.
*/
export async function warmAutopass() {
try {
await getAutopass()
return true
} catch (err) {
log.warn('AutoPass warm failed (invites unavailable until first create)', {
error: err.message,
})
return false
}
}
/**
* @param {import('autopass')} vault
*/
async function restoreLastInviteMeta(vault) {
if (lastInviteMeta) return
// Autopass stores at most one active invite in the view
let existing = null
try {
existing = await vault.base?.view?.findOne?.('@autopass/invite', {})
} catch {
existing = null
}
if (!existing) return
let jti = null
let role = Roles.operator
let expiresAt = new Date(Date.now() + 72 * 3600 * 1000).toISOString()
try {
const rec = await vault.get(PKG_KEY)
if (rec?.value) {
const pkg = typeof rec.value === 'string' ? JSON.parse(rec.value) : rec.value
jti = pkg.jti || null
role = pkg.role || role
expiresAt = pkg.expiresAt || expiresAt
}
} catch {
// ignore
}
// Invite string is not re-derived easily without z32 of existing.invite buffer
let inviteStr = null
try {
const z32 = (await import('z32')).default
if (existing.invite) inviteStr = z32.encode(existing.invite)
} catch {
inviteStr = null
}
if (!inviteStr) return
lastInviteMeta = {
invite: inviteStr,
role,
createdAt: new Date().toISOString(),
expiresAt,
jti: jti || 'unknown',
maxUses: 1,
note: 'restored-after-restart',
}
log.info('Restored active AutoPass invite metadata after restart')
}
export { PKG_KEY }