Governance

Grant, funding, and proposal review

Use this when reviewers score proposals, grants, milestones, maintainers, or deliverables and need protection from pressure by applicants, sponsors, or communities.

Use this when reviewers score proposals, grants, milestones, maintainers, or deliverables and need protection from pressure by applicants, sponsors, or communities.

Fit

  • Status: supported today.
  • NIP-85 kind: 30383 for a proposal event, or 30384 for an addressable proposal record.
  • Subject: proposal event id or NIP-33 address.
  • Helpers: aggregateEventContributions or aggregateAddressableContributions.
  • Proof version: v2 recommended.
  • Useful metrics: rank as review score, confidence, or funding priority.

Subject design

  • Use kind 30383 when the proposal is a single immutable event.
  • Use kind 30384 when the proposal, milestone, or deliverable is an addressable record that can be referenced by kind:pubkey:d-tag.
  • Score maintainers, proposals, milestones, and deliverables separately when the fund may take different actions on each.
  • Decide whether revisions inherit review scores. Funding workflows usually need explicit supersession when applicants materially change a proposal.

What to publish

  • A kind 30383 or 30384 assertion matching the proposal subject.
  • A rank profile explaining whether the score represents eligibility, technical confidence, impact, delivery risk, final funding priority, or a veto signal.
  • Proof v2 tags from the reviewer circle, plus threshold, conflict rules, expiry, appeal path, and whether the score is advisory or automatic.
  • Companion records for reviewer rationale, redacted comments, milestone evidence, conflict declarations, and final award decisions.

Implementation recipe

  1. Decide whether the subject is a proposal event, an addressable proposal record, a milestone, or a maintainer.
  2. Define whether rank means eligibility, technical confidence, impact, delivery risk, or final funding priority.
  3. Use proof v2 and verify the expected proposal subject, circle, threshold, and freshness.
  4. Keep reviewer comments, conflict declarations, and appeals in a companion grant process.
  5. Treat the aggregate as an auditable review signal unless the fund explicitly chooses automatic decisions.

Worked example

examples/use-cases/grant-funding-proposal-review.ts
import {
  NIP85_KINDS,
  aggregateAddressableContributions,
  contributeAddressableAssertion,
  createTrustCircle,
} from 'nostr-veil'
import {
  authorPubkey,
  defaultMembers,
  memberIndex,
  proofVersion,
  verifyUseCaseAssertion,
  withCreatedAt,
} from './_shared.js'

const slug = 'grant-funding-proposal-review'
const proposalAddress = `30023:${authorPubkey}:grant-2026-q2`
const circle = createTrustCircle(defaultMembers.map(member => member.pub))

const contributions = defaultMembers.map((reviewer, index) =>
  contributeAddressableAssertion(
    circle,
    proposalAddress,
    { rank: 68 + index * 5 },
    reviewer.priv,
    memberIndex(circle, reviewer.pub),
    { proofVersion },
  ),
)

export const assertion = withCreatedAt(aggregateAddressableContributions(
  circle,
  proposalAddress,
  contributions,
  { proofVersion },
))

export const result = verifyUseCaseAssertion(slug, assertion, {
  kind: NIP85_KINDS.ADDRESSABLE,
  subject: proposalAddress,
  subjectTag: 'a',
  circleId: circle.circleId,
  minDistinctSigners: 3,
  freshAfter: assertion.created_at - 300,
})

What to verify

  • Strict syntax and a valid proof v2.
  • The assertion kind and subject tag match the proposal or milestone currently being evaluated.
  • The reviewer circle is approved for this fund, topic, and conflict policy.
  • proof.distinctSigners meets the funding threshold and the rank meaning is known before ranking proposals.
  • The score is current for the proposal revision and not superseded by appeals, conflicts, or milestone evidence.

What this proves

  • Distinct grant reviewers contributed the aggregate score.
  • The score is bound to a specific proposal subject.
  • The reviewer set is public, but individual reviewers are not named.

What not to claim

  • Do not claim the proof predicts delivery or guarantees impact.
  • Do not claim hidden reviewers had no conflicts; conflict declarations and recusals must be enforced by the grant process.
  • Do not claim a score is a fair final decision unless the fund has explicitly published that policy.

Failure handling

  • Reject wrong-subject assertions, stale proposal scores, unknown reviewer circles, and scores without a published meaning.
  • Pause automatic ranking when conflict reports, appeals, or material proposal revisions appear.
  • Publish a new assertion for revised proposals or milestone outcomes instead of reinterpreting an older review.
  • Show final decision notes separately so applicants know what to fix even when reviewer identities remain protected.

Operational requirements

Risk to handleRequired control
The proof does not prove the proposal will succeed.Use milestone tracking, deliverable checks, and post-award review to validate execution.
The proof does not show private reviewer rationale.Publish redacted rationale, criteria scores, or decision notes when transparency is required.
Conflicts of interest are not eliminated by the proof.Require conflict declarations, recusals, reviewer eligibility rules, and independent review circles for large awards.
A score alone may not be a fair decision.Define whether the score is advisory, a ranking input, a veto, or an automatic threshold.

Policy choices

  • Does rank mean funding priority, technical confidence, impact, or eligibility?
  • Should the circle include domain experts, funders, community members, or all three?
  • Is the final decision automatic or advisory?
  • How are appeals and corrections handled?

NIP-85 kind reference

NIP-85 defines the assertion kind by the subject being scored. The kind number is part of the proof v2 context, so deployments should verify both the number and the subject hint tag.

30382 User assertion

Nostr pubkey subjects. Subject hint: p.

30383 Event assertion

Nostr event id subjects. Subject hint: e.

30384 Addressable event assertion

NIP-33 address subjects. Subject hint: a.

30385 NIP-73/external identifier assertion

packages, relays, domains, vendors, and other identifiers. Subject hint: k.

10040 Trusted service provider declaration

provider metadata, not a score assertion. Subject hint: provider tags.

Spec: NIP-85 trusted assertions.

Live relay test

The opt-in relay test signs this canonical example as real Nostr event data, publishes it to wss://relay.trotters.cc, fetches it back by id, and re-runs the application, syntax, Nostr signature, canonical tag, and proof checks.

Passed 2026-05-17T21:54:12.000Z
Events
1/1 fetched from relay
Proof
3/3 threshold from a 3-member ring
Run
tf7bic-3b3a5ae6d6
  • Canonical example passes locally
  • Relay stored and returned every signed event
  • Fetched Nostr event signatures are valid
  • Fetched tags match the canonical example
  • NIP-85 syntax validation passes
  • nostr-veil proof verification passes
  • Deployment profile verifier passes
875785599dc1...762e8647

Run the same check with npm run test:use-cases:relay -- --write docs/use-case-relay-checks.json.

Safety checks

Each canonical use-case example is also exercised by an adversarial test harness. These are the failure modes a production verifier should reject before acting on the score.

Tampered metric

Published scores must still match the signed contribution aggregate.

Wrong subject

The d tag and subject hint must stay bound to the signed v2 proof.

Wrong kind

The assertion kind must match the profile and the signed v2 context.

Proof downgrade

New deployment profiles require proof v2.

Duplicate signer

Repeated key images must not increase the signer count.

Insufficient threshold

Removing a signature must fail the profile threshold.

Stale assertion

created_at must remain inside the freshness window.

Unknown circle

The circle ID must be accepted by deployment policy.

Unsigned policy

verifyProductionDeployment() should require a signed deployment bundle from a trusted publisher.

Relay mutation

Fetched event content and tags must match the Nostr signature.

Use validateUseCaseProfileDefinition() for custom profiles, then verifyProductionDeployment() with trusted bundle publishers, signed relay events, accepted circle manifests, expected subject, freshness, and threshold policy so these checks are not left to application glue. For application UI and audit logs, use verifyProductionDeploymentReport() or createProductionDecisionReport() so failures include issue codes, remediation text, a recommended action, pass/fail/not-checked status for the controls, and the profile's proofClaims, proofLimitations, requiredControls, and recommendedActions.

Next examples