Nostr Event Schema Reference¶
CivicOS uses Nostr event kinds in the 1800-1899, 10800-10899, and 30800-30899 ranges. All events use secp256k1 Schnorr signatures (BIP-340).
This document provides the exact tag structures needed to construct valid CivicOS events in any language.
Event Categories¶
| Range | Category | Behavior |
|---|---|---|
| 30800-30899 | Addressable | Replaced by kind:pubkey:d-tag — one per identity per d-tag |
| 10800-10899 | Replaceable | Replaced by kind:pubkey — one per identity |
| 1800-1899 | Regular | Not replaceable — multiple allowed per identity |
Addressable Events (30800-30899)¶
Kind 30800: Voice¶
A citizen's stance on a civic entity. One voice per key per entity (new stance replaces old).
Tags:
[
["d", "decision:city-san-rafael:proudcity-city-san-rafael-city-council-february-3-2026-tuesday:06"],
["j", "city-san-rafael"],
["stance", "support"]
]
| Tag | Required | Values |
|---|---|---|
d | Yes | Entity identifier |
j | Yes | Jurisdiction ID |
stance | Yes | support, oppose, watching |
t | No | Topic tags (multiple allowed) |
Content: Empty string (or "revoked" to revoke)
Kind 30803: Comment¶
A public comment on a civic entity. One comment per key per entity.
Tags:
[
["d", "decision:city-san-rafael:proudcity-city-san-rafael-city-council-february-3-2026-tuesday:06"],
["j", "city-san-rafael"],
["stance", "support"]
]
| Tag | Required | Values |
|---|---|---|
d | Yes | Entity identifier |
j | No | Jurisdiction ID |
stance | No | support, oppose, watching |
Content: Comment text (or "deleted" to soft-delete)
Kind 30810: Civic Action¶
Defines an action that users can commit to and complete (e.g., attend a meeting, write a letter).
Tags:
| Tag | Required | Values |
|---|---|---|
d | Yes | Format: action:{initiative}:{type}:{hash} |
j | Yes | Jurisdiction ID |
Content: JSON object:
{
"initiative_id": "initiative:city-san-rafael:2026-03-01:abc123",
"action_type": "written_comment",
"description": "Write to the planning commission about the housing proposal",
"target": "planning@cityofsanrafael.org",
"deadline": "2026-03-15T00:00:00",
"template": "Dear Commissioners, ...",
"target_count": 50,
"coordination_url": "https://signal.group/..."
}
Action types: written_comment, attend_meeting, public_comment, contact_official, signature, share, custom
Kind 30811: Commitment¶
A user's commitment to take a civic action.
Tags:
| Tag | Required | Values |
|---|---|---|
d | Yes | Format: commit:{pubkey}:{action-d-tag} |
a | Yes | Reference to kind 30810 action |
Content: Empty string
Kind 30812: Completion¶
Evidence of completing a civic action.
Tags:
| Tag | Required | Values |
|---|---|---|
d | Yes | Format: complete:{pubkey}:{action-d-tag} |
a | Yes | Reference to kind 30810 action |
Content: JSON object:
{
"evidence_type": "email_confirmation",
"evidence_content": "Confirmation #12345",
"completed_at": "2026-03-10T14:30:00"
}
Evidence types: self_report, email_confirmation, attendance_check, verified
Kind 30850: Attestation¶
Proof of physical presence, issued by a trusted organization's signer (not the subject).
Tags:
[
["d", "attest:city-san-rafael:subject_pubkey_hex"],
["p", "subject_pubkey_hex"],
["j", "city-san-rafael"],
["type", "physical"]
]
| Tag | Required | Values |
|---|---|---|
d | Yes | Format: attest:{jurisdiction}:{subject_pubkey} |
p | Yes | Subject's public key |
j | Yes | Jurisdiction |
type | Yes | physical, community-vouched, mail |
Content: "civicos:attestation:v1:{jurisdiction}:{type}:{created_at}"
Signed by: Issuer keypair (not the subject). One attestation per pubkey per jurisdiction.
Kind 30801: Entity¶
A civic entity record (decision, meeting, agenda item, initiative).
Tags:
[
["d", "decision:city-san-rafael:proudcity-city-san-rafael-city-council-february-3-2026-tuesday:06"],
["j", "city-san-rafael"],
["type", "decision"],
["title", "Housing Accountability Act Amendment"],
["t", "housing"],
["t", "zoning"]
]
| Tag | Required | Values |
|---|---|---|
d | Yes | Entity identifier |
j | Yes | Jurisdiction |
type | Yes | decision, initiative, agenda_item, meeting |
title | Yes | Human-readable title |
t | No | Topic tags (multiple allowed) |
Content: JSON with type-specific fields
Kind 30802: Subscription¶
Topic/event subscription with encrypted delivery config.
Tags:
[
["d", "sub:city-san-rafael:housing"],
["j", "city-san-rafael"],
["t", "housing"],
["threshold", "10"]
]
| Tag | Required | Values |
|---|---|---|
d | Yes | Subscription identifier |
j | No | Jurisdiction filter |
t | No | Topic filters (multiple allowed) |
type | No | Entity type filter |
threshold | No | Voice count threshold for notification |
Content: NIP-44 encrypted JSON with delivery config (endpoint, preferences)
Replaceable Events (10800-10899)¶
Kind 10800: Provenance¶
Self-signed reputation record. One per pubkey, updated as participation grows.
Tags:
[
["first-voice", "2025-09-01"],
["total-voices", "42"],
["entities-touched", "15"],
["j", "city-san-rafael"],
["attestation", "physical", "city-san-rafael", "2026-01-15"]
]
| Tag | Required | Values |
|---|---|---|
first-voice | No | ISO date of first voice |
total-voices | No | Total voice count |
entities-touched | No | Unique entities count |
j | No | Primary jurisdiction |
attestation | No | Format: [type, jurisdiction, date] (multiple allowed) |
Content: Empty string
Regular Events (1800-1899)¶
Kind 1800: Vouch¶
One citizen vouching for another (social attestation).
Tags:
| Tag | Required | Values |
|---|---|---|
p | Yes | Vouchee's public key |
j | No | Jurisdiction context |
Content: Optional description of relationship
Kind 1801: Event Notification¶
Relay-generated notification about civic events.
Tags:
[
["event-type", "agenda_published"],
["j", "city-san-rafael"],
["a", "30801:pubkey:meeting:city-san-rafael:2026-02-03"]
]
| Tag | Required | Values |
|---|---|---|
event-type | Yes | agenda_published, decision_made, meeting_scheduled, public_comment_opened, public_comment_closing, voice_threshold_reached, initiative_created |
j | Yes | Jurisdiction |
a | No | Reference to addressable event |
Content: JSON notification payload
Signed by: Relay's keypair
Kind 1802: Key Link Attestation¶
Links an old CivicOS key (SECP256R1) to a new Nostr key (secp256k1).
Tags:
The old-sig signs the string "civicos:link:v1:{new_pubkey}". The new key signs the Nostr event normally. The relay validates both signatures and merges provenance.
Kind 1804: Feedback¶
User feedback on the platform. Multiple submissions per user allowed.
Tags:
| Tag | Required | Values |
|---|---|---|
t | Yes | bug, feature, general |
j | Yes | Jurisdiction |
v | Yes | Schema version (currently "1") |
Content: Free-text feedback body
Tag Reference¶
| Tag | Name | Used In |
|---|---|---|
d | Identifier | All addressable events |
j | Jurisdiction | All events |
t | Topic | Voices, entities, subscriptions, feedback |
p | Pubkey reference | Attestations, vouches |
e | Event reference | General |
a | Addressable event ref | Commitments, completions, notifications |
stance | Stance | Voices, comments |
type | Type | Entities, attestations |
title | Title | Entities |
threshold | Threshold | Subscriptions |
event-type | Event type | Notifications |
old-key | Legacy key | Key link attestations |
old-sig | Legacy signature | Key link attestations |
first-voice | First voice date | Provenance |
total-voices | Voice count | Provenance |
entities-touched | Entity count | Provenance |
attestation | Attestation record | Provenance |
v | Version | Feedback |
Signature Verification¶
All events use BIP-340 Schnorr signatures over secp256k1. The relay verifies every write:
- Reconstruct the serialized event:
[0, pubkey, created_at, kind, tags, content] - SHA-256 hash the JSON serialization
- Verify the Schnorr signature against the public key
- Check
created_atis present and within acceptable range - Check
jurisdictiontag is present (required for all civic events)