Skip to content

Commit 15bacad

Browse files
committed
Align rstate with ContextVM SDK 0.13 lifecycle
ContextVM 0.13 changes the server announcement and payment interaction API, so rstate now targets the current SDK contract while preserving the existing transparent payment mode as an opt-in compatibility setting. Constraint: @contextvm/sdk 0.13.9 is inside the pnpm minimum-release-age cooldown but is the requested latest SDK line Rejected: Keep deprecated isPublicServer usage | 0.13 exposes isAnnouncedServer instead Rejected: Hard-code transparent payments | 0.13 introduces optional payment interaction as the lifecycle default Confidence: high Scope-risk: moderate Directive: Keep CVM_PAYMENT_INTERACTION_POLICY limited to SDK-supported server policies unless ContextVM adds new server-side values Tested: pnpm -C apps/rstate run type-check Tested: pnpm -C apps/rstate exec vitest run Tested: pnpm -C apps/rstate run build Tested: pnpm peers check --filter @nostr-watch/rstate Not-tested: Live CVM relay/payment flow against deployed NWC wallet
1 parent 24f897e commit 15bacad

8 files changed

Lines changed: 266 additions & 152 deletions

File tree

apps/rstate/.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ CVM_AUTH_ALLOW_ANY=false
3333
# CEP-8 CVM Payments
3434
CVM_PAYMENTS_ENABLED=false
3535
CVM_NWC_CONNECTION_STRING=
36+
CVM_PAYMENT_INTERACTION_POLICY=optional
3637
# CVM_PRICING_YAML=./config/pricing.cvm.yaml # optional CVM-specific overrides
3738

3839
# ========================================

apps/rstate/package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,10 @@
3535
"docker:stop": "docker-compose down"
3636
},
3737
"dependencies": {
38-
"@contextvm/sdk": "^0.6.2",
38+
"@contextvm/sdk": "^0.13.9",
3939
"@fastify/cors": "^11.1.0",
4040
"@fastify/swagger": "^9.5.2",
4141
"@modelcontextprotocol/sdk": "^1.26.0",
42-
"@noble/hashes": "^1.5.0",
4342
"@noble/secp256k1": "^2.1.0",
4443
"@nostrwatch/announce": "workspace:^",
4544
"@nostrwatch/publisher": "workspace:^",
@@ -60,7 +59,7 @@
6059
"@types/ws": "^8.5.13",
6160
"tsup": "^8.3.5",
6261
"tsx": "^4.19.2",
63-
"typescript": "^5.7.2",
62+
"typescript": "^5.9.3",
6463
"vitest": "^3.2.6"
6564
},
6665
"engines": {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { PaymentInteractionPolicy } from '@contextvm/sdk'
2+
3+
const DEFAULT_CVM_PAYMENT_INTERACTION_POLICY: PaymentInteractionPolicy = 'optional'
4+
5+
export function resolveCvmPaymentInteractionPolicy(
6+
env: { [key: string]: string | undefined } = process.env
7+
): PaymentInteractionPolicy {
8+
const rawPolicy = env.CVM_PAYMENT_INTERACTION_POLICY?.trim()
9+
10+
if (!rawPolicy) return DEFAULT_CVM_PAYMENT_INTERACTION_POLICY
11+
if (rawPolicy === 'optional' || rawPolicy === 'transparent') return rawPolicy
12+
13+
throw new Error(
14+
`CVM_PAYMENT_INTERACTION_POLICY must be "optional" or "transparent"; got "${rawPolicy}"`
15+
)
16+
}

apps/rstate/src/resilient-relay-pool.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ export class ResilientRelayPool implements RelayPool {
171171
this.subscriptions.clear()
172172
logger.info('RelayHandler: all subscriptions unsubscribed')
173173
},
174+
getRelayUrls: () => this.relays.map(relay => relay.url),
174175
}
175176
}
176177

apps/rstate/src/server.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
88
import { NostrServerTransport, PrivateKeySigner, EncryptionMode, withServerPayments, LnBolt11NwcPaymentProcessor } from '@contextvm/sdk'
99
import { loadPricedCapabilities } from './payments/cvm-pricing.js'
10+
import { resolveCvmPaymentInteractionPolicy } from './payments/cvm-payment-policy.js'
1011
import { ResilientRelayPool } from './resilient-relay-pool.js'
1112
import type { Config } from './config.js'
1213
import { getLogger } from './utils/logger.js'
@@ -207,7 +208,7 @@ export class CVMServer {
207208
name: 'RelayVM',
208209
about: 'ContextVM server that aggregates NIP-66 relay intelligence and exposes it via MCP over Nostr. Service provided by nostr.watch',
209210
},
210-
isPublicServer: cvmConfig.allowedPubkeys.length === 0,
211+
isAnnouncedServer: cvmConfig.allowedPubkeys.length === 0,
211212
allowedPublicKeys: cvmConfig.allowedPubkeys.length > 0 ? cvmConfig.allowedPubkeys : undefined,
212213
})
213214

@@ -224,13 +225,18 @@ export class CVMServer {
224225
})
225226

226227
const pricedCapabilities = loadPricedCapabilities()
228+
const paymentInteraction = resolveCvmPaymentInteractionPolicy()
227229

228230
this.transport = withServerPayments(this.transport, {
229231
processors: [nwcProcessor],
230232
pricedCapabilities,
233+
paymentInteraction,
231234
})
232235

233-
logger.info({ pricedTools: pricedCapabilities.length }, 'CVM payment gating enabled (CEP-8)')
236+
logger.info({
237+
pricedTools: pricedCapabilities.length,
238+
paymentInteraction,
239+
}, 'CVM payment gating enabled (CEP-8)')
234240
}
235241

236242
// DISABLED: Subscription system
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { resolveCvmPaymentInteractionPolicy } from '../src/payments/cvm-payment-policy.js'
3+
4+
describe('CVM payment interaction policy', () => {
5+
it('defaults to the ContextVM 0.13 optional lifecycle policy', () => {
6+
expect(resolveCvmPaymentInteractionPolicy({})).toBe('optional')
7+
})
8+
9+
it('accepts transparent-only compatibility mode', () => {
10+
expect(resolveCvmPaymentInteractionPolicy({ CVM_PAYMENT_INTERACTION_POLICY: 'transparent' })).toBe('transparent')
11+
})
12+
13+
it('accepts the explicit optional policy value', () => {
14+
expect(resolveCvmPaymentInteractionPolicy({ CVM_PAYMENT_INTERACTION_POLICY: ' optional ' })).toBe('optional')
15+
})
16+
17+
it('rejects wire-level payment interaction modes as server policy values', () => {
18+
expect(() => resolveCvmPaymentInteractionPolicy({
19+
CVM_PAYMENT_INTERACTION_POLICY: 'explicit_gating',
20+
})).toThrow('CVM_PAYMENT_INTERACTION_POLICY must be "optional" or "transparent"')
21+
})
22+
})

0 commit comments

Comments
 (0)