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, FOLLOWS,
MESSAGING_RELAYS, MESSAGING_RELAYS,
PROFILE, PROFILE,
RELAY_INVITE,
RELAYS, RELAYS,
type ManagementResponse,
} from "@welshman/util" } 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 {Sync, User, publish} from "@welshman/app"
import {stripPrefix} from "@lib/util" 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 {PLATFORM_URL} from "@app/env"
import {relaysMostlyRestricted} from "@app/policies" import {relaysMostlyRestricted} from "@app/policies"
import {Push} from "@app/push" import {Push} from "@app/push"
@ -347,34 +347,44 @@ export class Access {
async prepareInvite(h?: string) { async prepareInvite(h?: string) {
this.loading.set(true) this.loading.set(true)
// A request that times out or hits a closed socket resolves with no events, so an try {
// empty result is the only signal we get that the relay never answered. const management = relayManagement.get().forUrl(this.url)
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),
])
if (events[0]) { const [{result: methods}, roomInviteResult] = await Promise.all([
const eventReader = reader(RelayInvite) management.supportedMethods().catch((error): ManagementResponse => {
const invite = await eventReader(events[0]) 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 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]) => { export const deriveSpaceSupportedMethods = simpleCache(([url]: [string | undefined]) => {
let checkedAt = 0 let checkedAt = 0