{"id":"f2b2be10cb1ba9877ec1adc7053a446085f2a01a5b66c1c453d5cfb08b87afe8","pubkey":"d70d50091504b992d1838822af245d5f6b3a16b82d917acb7924cef61ed4acee","created_at":1779394561,"kind":30817,"tags":[["d","committees"],["title","Committees"],["k","30068"],["k","39068"],["k","39069"],["client","nostrhub.io"]],"content":"# Committee Structures\n\n`draft` `optional`\n\nThis NIP defines a committee management system for Nostr, enabling communities to create organizational structures with members, roles, and open positions. Committees use a multi-event architecture that separates board metadata from members and openings, allowing for flexible organizational management.\n\n## Motivation\n\nBitcoin community groups (meetups, hack spaces, conferences) need lightweight organizational structures — committees with chairs, members, and open volunteer positions. Existing Nostr kinds don't provide a way to express these relationships.\n\nWhile NIP-51 lists can group pubkeys, they lack the semantics for roles (chair, vice-chair, member), open positions, and the committee metadata (meeting schedules, images, descriptions) that real-world organizations need.\n\nThis NIP provides three event kinds that work together:\n\n- **Committee** (kind 30068) — The group itself (parameterized replaceable)\n- **Committee Member** (kind 39068) — A person belonging to a committee\n- **Committee Opening** (kind 39069) — An open position on a committee\n\n## Event Kinds\n\n### Kind 30068: Committee\n\nA parameterized replaceable event representing a committee or working group.\n\n**Required Tags:**\n\n| Tag | Description |\n| --- | --- |\n| `d` | Unique identifier (slug) for the committee |\n\n**Optional Tags:**\n\n| Tag | Description |\n| --- | --- |\n| `title` | Human-readable committee name |\n| `description` | Extended description of the committee's purpose |\n| `image` | Committee logo or cover image URL |\n| `meetingSchedule` | Recurring meeting schedule (free-form text, e.g. \"2nd Thursday of each month at 7pm\") |\n| `openings` | Number of open positions (integer as string) |\n| `t` | Hashtag for categorization (repeatable) |\n\n**Content:** Empty string or optional description text.\n\n**Example:**\n\n```json\n{\n  \"kind\": 30068,\n  \"pubkey\": \"<committee-author-pubkey>\",\n  \"created_at\": 1704067200,\n  \"content\": \"Organizes monthly meetup logistics including venue, speakers, and refreshments.\",\n  \"tags\": [\n    [\"d\", \"events-committee\"],\n    [\"title\", \"Events Committee\"],\n    [\"description\", \"Organizes monthly meetup logistics\"],\n    [\"image\", \"https://example.com/events-committee.png\"],\n    [\"meetingSchedule\", \"1st Wednesday of each month at 6pm\"],\n    [\"openings\", \"3\"],\n    [\"t\", \"events\"],\n    [\"t\", \"meetup\"]\n  ]\n}\n```\n\n**Committee Address Format:** `30068:<pubkey>:<d-tag>`\n\n### Kind 39068: Committee Member\n\nA regular event representing a person's membership in a committee. Each member is a separate event, enabling per-member role tracking and independent updates.\n\n**Required Tags:**\n\n| Tag | Description |\n| --- | --- |\n| `a` | Committee coordinate: `30068:<pubkey>:<d-tag>` |\n| `d` | Unique identifier for this membership record |\n| `role` | Member's role (e.g. `chair`, `vice-chair`, `member`) |\n| `name` | Display name of the member |\n\n**Optional Tags:**\n\n| Tag | Description |\n| --- | --- |\n| `email` | Contact email address |\n| `phone` | Contact phone number |\n| `p` | Nostr pubkey of the member (for linking to their Nostr identity) |\n\n**Content:** Empty string.\n\n**Example:**\n\n```json\n{\n  \"kind\": 39068,\n  \"pubkey\": \"<committee-author-pubkey>\",\n  \"created_at\": 1704067200,\n  \"content\": \"\",\n  \"tags\": [\n    [\"a\", \"30068:<committee-pubkey>:events-committee\"],\n    [\"d\", \"alice-membership\"],\n    [\"role\", \"chair\"],\n    [\"name\", \"Alice\"],\n    [\"email\", \"alice@example.com\"],\n    [\"p\", \"<alice-nostr-pubkey>\"]\n  ]\n}\n```\n\n### Kind 39069: Committee Opening\n\nA regular event representing an open position on a committee. Openings can be created and deleted independently of members.\n\n**Required Tags:**\n\n| Tag | Description |\n| --- | --- |\n| `a` | Committee coordinate: `30068:<pubkey>:<d-tag>` |\n| `d` | Unique identifier for this opening |\n| `title` | Title of the open position |\n\n**Optional Tags:**\n\n| Tag | Description |\n| --- | --- |\n| `description` | Description of responsibilities for the position |\n\n**Content:** Empty string or optional description text.\n\n**Example:**\n\n```json\n{\n  \"kind\": 39069,\n  \"pubkey\": \"<committee-author-pubkey>\",\n  \"created_at\": 1704067200,\n  \"content\": \"Help coordinate speakers and manage the event calendar.\",\n  \"tags\": [\n    [\"a\", \"30068:<committee-pubkey>:events-committee\"],\n    [\"d\", \"speaker-coordinator-opening\"],\n    [\"title\", \"Speaker Coordinator\"],\n    [\"description\", \"Find and coordinate speakers for monthly meetups\"]\n  ]\n}\n```\n\n## Client Behavior\n\n### Fetching Committees\n\n```\n{\n  \"kinds\": [30068],\n  \"authors\": [\"<whitelisted-pubkeys>\"]\n}\n```\n\nClients should deduplicate by coordinate (`kind:pubkey:d-tag`), keeping the most recent event.\n\n### Fetching Members\n\n```\n{\n  \"kinds\": [39068],\n  \"authors\": [\"<whitelisted-pubkeys>\"],\n  \"#a\": [\"30068:<pubkey>:<d-tag>\"]\n}\n```\n\nMembers should be displayed sorted by role (chair first, then vice-chair, then members), then alphabetically by name.\n\n### Fetching Openings\n\n```\n{\n  \"kinds\": [39069],\n  \"authors\": [\"<whitelisted-pubkeys>\"],\n  \"#a\": [\"30068:<pubkey>:<d-tag>\"]\n}\n```\n\n### Role Values\n\nCommon role values and their suggested display order:\n\n| Role | Sort Priority |\n| --- | --- |\n| `chair` | 0 (first) |\n| `vice-chair` | 1 |\n| `member` | 2 |\n\nClients may encounter custom role values not listed above — these should sort after the standard roles.\n\n### Displaying Committees\n\n1. Fetch all committee events (kind 30068)\n2. For each committee, fetch members (kind 39068) and openings (kind 39069) by `a` tag\n3. Display members grouped by role with contact info\n4. Display openings as actionable items users can express interest in\n\n## Deletion\n\nCommittees, members, and openings can be deleted using NIP-09 deletion events referencing the event ID or using NIP-33 `a` tag deletion for parameterized replaceable events (kind 30068).\n\n## Access Control\n\nThis NIP does not define on-chain access control. Implementations typically restrict publishing to a whitelist of authorized pubkeys maintained by the community. The `authors` filter ensures only authorized events are displayed.\n\n## References\n\n- NIP-01: Basic protocol flow\n- NIP-09: Event deletion\n- NIP-33: Parameterized replaceable events","sig":"cf41043a0e87aa315216ddce33add28ddbbaa1530a5484c244d13ff0ac933d38d899055d629dffbcb443ef4ac6c50e0fff5883f07577480eadd52a8dca3a4ba4"}