Use nip 86 for claim management

This commit is contained in:
Jon Staab 2026-08-18 12:55:08 -07:00
parent 8a0918d551
commit 0a9d3cfb49
2 changed files with 39 additions and 32 deletions

View file

@ -10,13 +10,13 @@ import {
FOLLOWS,
MESSAGING_RELAYS,
PROFILE,
RELAY_INVITE,
RELAYS,
type ManagementResponse,
} from "@welshman/util"
import {RelayInvite, RelayJoin, RelayLeave, RoomJoin} from "@welshman/domain"
import {RelayJoin, RelayLeave, RoomJoin} from "@welshman/domain"
import {Sync, User, publish} from "@welshman/app"
import {stripPrefix} from "@lib/util"
import {app, command, network, reader, roomLists, thunks, writer} from "@app/core"
import {app, command, relayManagement, roomLists, thunks, writer} from "@app/core"
import {PLATFORM_URL} from "@app/env"
import {relaysMostlyRestricted} from "@app/policies"
import {Push} from "@app/push"
@ -347,34 +347,44 @@ export class Access {
async prepareInvite(h?: string) {
this.loading.set(true)
// A request that times out or hits a closed socket resolves with no events, so an
// empty result is the only signal we get that the relay never answered.
const [events, roomInviteResult] = await Promise.all([
network.get().request({
relays: [this.url],
autoClose: true,
signal: AbortSignal.timeout(10000),
filters: [{kinds: [RELAY_INVITE]}],
}),
h ? publishRoomInvite(this.url, h) : Promise.resolve({code: undefined, error: undefined}),
// Keep the spinner up long enough that a fast relay doesn't make it flash
sleep(300),
])
try {
const management = relayManagement.get().forUrl(this.url)
if (events[0]) {
const eventReader = reader(RelayInvite)
const invite = await eventReader(events[0])
const [{result: methods}, roomInviteResult] = await Promise.all([
management.supportedMethods().catch((error): ManagementResponse => {
console.error(error)
return {}
}),
h ? publishRoomInvite(this.url, h) : Promise.resolve({code: undefined, error: undefined}),
// Keep the spinner up long enough that a fast relay doesn't make it flash
sleep(300),
])
this.claim.set(invite.claim() ?? "")
// The relay reports methods relay-wide rather than per-user, so a listed method can
// still come back "blocked" for this particular user — treat that as having no claim.
if (methods?.includes("createclaim")) {
const {result: claims} = await management.listClaims()
if (claims?.[0]) {
this.claim.set(claims[0])
} else {
const claim = randomId()
const {error} = await management.createClaim(claim)
if (error) {
this.claimFailed.set(true)
} else {
this.claim.set(claim)
}
}
}
if (h) {
this.roomCode.set(roomInviteResult.code)
this.roomInviteError.set(roomInviteResult.error)
}
} finally {
this.loading.set(false)
}
this.claimFailed.set(events.length === 0)
if (h) {
this.roomCode.set(roomInviteResult.code)
this.roomInviteError.set(roomInviteResult.error)
}
this.loading.set(false)
}
}

View file

@ -21,9 +21,6 @@ export const deriveSpaceBannedPubkeyItems = (url: string) => {
return store
}
// A relay only answers NIP-86 for its admins, so the methods it reports double as the user's
// permissions. This costs a signature and an http round trip, so share one store per url and
// only re-check when the answer has had time to go stale (or when a check failed).
export const deriveSpaceSupportedMethods = simpleCache(([url]: [string | undefined]) => {
let checkedAt = 0