[{"data":1,"prerenderedAt":1140},["ShallowReactive",2],{"navigation":3,"\u002Fv0\u002Fabantu\u002Fplugins-and-extensibility":489,"\u002Fv0\u002Fabantu\u002Fplugins-and-extensibility-surround":1135},[4,287],{"title":5,"path":6,"stem":7,"children":8,"icon":286},"v1 · in progress","\u002Fv1","1.v1\u002F01.index",[9,11,46],{"title":10,"path":6,"stem":7},"The current guide",{"title":12,"path":13,"stem":14,"children":15,"icon":45},"Communities","\u002Fv1\u002Fcommunities","1.v1\u002F03.communities\u002F1.index",[16,17,21,25,29,33,37,41],{"title":12,"path":13,"stem":14},{"title":18,"path":19,"stem":20},"Decide together","\u002Fv1\u002Fcommunities\u002Fdecide-together","1.v1\u002F03.communities\u002F2.decide-together",{"title":22,"path":23,"stem":24},"Protocols","\u002Fv1\u002Fcommunities\u002Fprotocols","1.v1\u002F03.communities\u002F3.protocols",{"title":26,"path":27,"stem":28},"Features and settings","\u002Fv1\u002Fcommunities\u002Ffeatures","1.v1\u002F03.communities\u002F4.features",{"title":30,"path":31,"stem":32},"Custodians and the community key","\u002Fv1\u002Fcommunities\u002Fcustodians","1.v1\u002F03.communities\u002F5.custodians",{"title":34,"path":35,"stem":36},"Projects","\u002Fv1\u002Fcommunities\u002Fprojects","1.v1\u002F03.communities\u002F6.projects",{"title":38,"path":39,"stem":40},"Between communities","\u002Fv1\u002Fcommunities\u002Fbetween-communities","1.v1\u002F03.communities\u002F7.between-communities",{"title":42,"path":43,"stem":44},"Messages, relays and storage","\u002Fv1\u002Fcommunities\u002Fmessages-and-storage","1.v1\u002F03.communities\u002F8.messages-and-storage","i-lucide-users",{"title":47,"path":48,"stem":49,"children":50,"icon":285},"Technical reference","\u002Fv1\u002Freference","1.v1\u002F12.reference\u002F1.index",[51,52,274,278,282],{"title":47,"path":48,"stem":49},{"title":53,"path":54,"stem":55,"children":56},"Packages","\u002Fv1\u002Freference\u002Fpackages","1.v1\u002F12.reference\u002F2.packages\u002F1.index",[57,58,62,66,70,74,78,82,86,90,94,98,102,106,110,114,118,122,126,130,134,138,142,146,150,154,158,162,166,170,174,178,182,186,190,194,198,202,206,210,214,218,222,226,230,234,238,242,246,250,254,258,262,266,270],{"title":53,"path":54,"stem":55},{"title":59,"path":60,"stem":61},"aes-gcm","\u002Fv1\u002Freference\u002Fpackages\u002Faes-gcm","1.v1\u002F12.reference\u002F2.packages\u002Faes-gcm",{"title":63,"path":64,"stem":65},"agreements","\u002Fv1\u002Freference\u002Fpackages\u002Fagreements","1.v1\u002F12.reference\u002F2.packages\u002Fagreements",{"title":67,"path":68,"stem":69},"auth-sdk","\u002Fv1\u002Freference\u002Fpackages\u002Fauth-sdk","1.v1\u002F12.reference\u002F2.packages\u002Fauth-sdk",{"title":71,"path":72,"stem":73},"blind-tokens","\u002Fv1\u002Freference\u002Fpackages\u002Fblind-tokens","1.v1\u002F12.reference\u002F2.packages\u002Fblind-tokens",{"title":75,"path":76,"stem":77},"blob-store","\u002Fv1\u002Freference\u002Fpackages\u002Fblob-store","1.v1\u002F12.reference\u002F2.packages\u002Fblob-store",{"title":79,"path":80,"stem":81},"broadcast-medium","\u002Fv1\u002Freference\u002Fpackages\u002Fbroadcast-medium","1.v1\u002F12.reference\u002F2.packages\u002Fbroadcast-medium",{"title":83,"path":84,"stem":85},"capacitor-keystore","\u002Fv1\u002Freference\u002Fpackages\u002Fcapacitor-keystore","1.v1\u002F12.reference\u002F2.packages\u002Fcapacitor-keystore",{"title":87,"path":88,"stem":89},"capacitor-tor","\u002Fv1\u002Freference\u002Fpackages\u002Fcapacitor-tor","1.v1\u002F12.reference\u002F2.packages\u002Fcapacitor-tor",{"title":91,"path":92,"stem":93},"capacitor-unifiedpush","\u002Fv1\u002Freference\u002Fpackages\u002Fcapacitor-unifiedpush","1.v1\u002F12.reference\u002F2.packages\u002Fcapacitor-unifiedpush",{"title":95,"path":96,"stem":97},"capture-inbox","\u002Fv1\u002Freference\u002Fpackages\u002Fcapture-inbox","1.v1\u002F12.reference\u002F2.packages\u002Fcapture-inbox",{"title":99,"path":100,"stem":101},"clan","\u002Fv1\u002Freference\u002Fpackages\u002Fclan","1.v1\u002F12.reference\u002F2.packages\u002Fclan",{"title":103,"path":104,"stem":105},"community-core","\u002Fv1\u002Freference\u002Fpackages\u002Fcommunity-core","1.v1\u002F12.reference\u002F2.packages\u002Fcommunity-core",{"title":107,"path":108,"stem":109},"community-identity","\u002Fv1\u002Freference\u002Fpackages\u002Fcommunity-identity","1.v1\u002F12.reference\u002F2.packages\u002Fcommunity-identity",{"title":111,"path":112,"stem":113},"community-log","\u002Fv1\u002Freference\u002Fpackages\u002Fcommunity-log","1.v1\u002F12.reference\u002F2.packages\u002Fcommunity-log",{"title":115,"path":116,"stem":117},"community-plugin-api","\u002Fv1\u002Freference\u002Fpackages\u002Fcommunity-plugin-api","1.v1\u002F12.reference\u002F2.packages\u002Fcommunity-plugin-api",{"title":119,"path":120,"stem":121},"corroboration","\u002Fv1\u002Freference\u002Fpackages\u002Fcorroboration","1.v1\u002F12.reference\u002F2.packages\u002Fcorroboration",{"title":123,"path":124,"stem":125},"cover-traffic-scheduler","\u002Fv1\u002Freference\u002Fpackages\u002Fcover-traffic-scheduler","1.v1\u002F12.reference\u002F2.packages\u002Fcover-traffic-scheduler",{"title":127,"path":128,"stem":129},"demo-family-tree","\u002Fv1\u002Freference\u002Fpackages\u002Fdemo-family-tree","1.v1\u002F12.reference\u002F2.packages\u002Fdemo-family-tree",{"title":131,"path":132,"stem":133},"device-link","\u002Fv1\u002Freference\u002Fpackages\u002Fdevice-link","1.v1\u002F12.reference\u002F2.packages\u002Fdevice-link",{"title":135,"path":136,"stem":137},"device-scanner","\u002Fv1\u002Freference\u002Fpackages\u002Fdevice-scanner","1.v1\u002F12.reference\u002F2.packages\u002Fdevice-scanner",{"title":139,"path":140,"stem":141},"did-e2e","\u002Fv1\u002Freference\u002Fpackages\u002Fdid-e2e","1.v1\u002F12.reference\u002F2.packages\u002Fdid-e2e",{"title":143,"path":144,"stem":145},"directory-listings","\u002Fv1\u002Freference\u002Fpackages\u002Fdirectory-listings","1.v1\u002F12.reference\u002F2.packages\u002Fdirectory-listings",{"title":147,"path":148,"stem":149},"ecosystem-cli","\u002Fv1\u002Freference\u002Fpackages\u002Fecosystem-cli","1.v1\u002F12.reference\u002F2.packages\u002Fecosystem-cli",{"title":151,"path":152,"stem":153},"endscroll","\u002Fv1\u002Freference\u002Fpackages\u002Fendscroll","1.v1\u002F12.reference\u002F2.packages\u002Fendscroll",{"title":155,"path":156,"stem":157},"ephemeral-share","\u002Fv1\u002Freference\u002Fpackages\u002Fephemeral-share","1.v1\u002F12.reference\u002F2.packages\u002Fephemeral-share",{"title":159,"path":160,"stem":161},"example-community-plugin","\u002Fv1\u002Freference\u002Fpackages\u002Fexample-community-plugin","1.v1\u002F12.reference\u002F2.packages\u002Fexample-community-plugin",{"title":163,"path":164,"stem":165},"family","\u002Fv1\u002Freference\u002Fpackages\u002Ffamily","1.v1\u002F12.reference\u002F2.packages\u002Ffamily",{"title":167,"path":168,"stem":169},"geo","\u002Fv1\u002Freference\u002Fpackages\u002Fgeo","1.v1\u002F12.reference\u002F2.packages\u002Fgeo",{"title":171,"path":172,"stem":173},"geo-claims","\u002Fv1\u002Freference\u002Fpackages\u002Fgeo-claims","1.v1\u002F12.reference\u002F2.packages\u002Fgeo-claims",{"title":175,"path":176,"stem":177},"geo-packs","\u002Fv1\u002Freference\u002Fpackages\u002Fgeo-packs","1.v1\u002F12.reference\u002F2.packages\u002Fgeo-packs",{"title":179,"path":180,"stem":181},"geo-vue","\u002Fv1\u002Freference\u002Fpackages\u002Fgeo-vue","1.v1\u002F12.reference\u002F2.packages\u002Fgeo-vue",{"title":183,"path":184,"stem":185},"historical-date","\u002Fv1\u002Freference\u002Fpackages\u002Fhistorical-date","1.v1\u002F12.reference\u002F2.packages\u002Fhistorical-date",{"title":187,"path":188,"stem":189},"identity","\u002Fv1\u002Freference\u002Fpackages\u002Fidentity","1.v1\u002F12.reference\u002F2.packages\u002Fidentity",{"title":191,"path":192,"stem":193},"identity-scope","\u002Fv1\u002Freference\u002Fpackages\u002Fidentity-scope","1.v1\u002F12.reference\u002F2.packages\u002Fidentity-scope",{"title":195,"path":196,"stem":197},"inquiry-board","\u002Fv1\u002Freference\u002Fpackages\u002Finquiry-board","1.v1\u002F12.reference\u002F2.packages\u002Finquiry-board",{"title":199,"path":200,"stem":201},"inzira","\u002Fv1\u002Freference\u002Fpackages\u002Finzira","1.v1\u002F12.reference\u002F2.packages\u002Finzira",{"title":203,"path":204,"stem":205},"isoko-client","\u002Fv1\u002Freference\u002Fpackages\u002Fisoko-client","1.v1\u002F12.reference\u002F2.packages\u002Fisoko-client",{"title":207,"path":208,"stem":209},"link-room","\u002Fv1\u002Freference\u002Fpackages\u002Flink-room","1.v1\u002F12.reference\u002F2.packages\u002Flink-room",{"title":211,"path":212,"stem":213},"notices","\u002Fv1\u002Freference\u002Fpackages\u002Fnotices","1.v1\u002F12.reference\u002F2.packages\u002Fnotices",{"title":215,"path":216,"stem":217},"one-time-mailbox","\u002Fv1\u002Freference\u002Fpackages\u002Fone-time-mailbox","1.v1\u002F12.reference\u002F2.packages\u002Fone-time-mailbox",{"title":219,"path":220,"stem":221},"portable-entitlements","\u002Fv1\u002Freference\u002Fpackages\u002Fportable-entitlements","1.v1\u002F12.reference\u002F2.packages\u002Fportable-entitlements",{"title":223,"path":224,"stem":225},"protocol-catalog","\u002Fv1\u002Freference\u002Fpackages\u002Fprotocol-catalog","1.v1\u002F12.reference\u002F2.packages\u002Fprotocol-catalog",{"title":227,"path":228,"stem":229},"protocol-core","\u002Fv1\u002Freference\u002Fpackages\u002Fprotocol-core","1.v1\u002F12.reference\u002F2.packages\u002Fprotocol-core",{"title":231,"path":232,"stem":233},"redact","\u002Fv1\u002Freference\u002Fpackages\u002Fredact","1.v1\u002F12.reference\u002F2.packages\u002Fredact",{"title":235,"path":236,"stem":237},"relay-envelope","\u002Fv1\u002Freference\u002Fpackages\u002Frelay-envelope","1.v1\u002F12.reference\u002F2.packages\u002Frelay-envelope",{"title":239,"path":240,"stem":241},"requirements","\u002Fv1\u002Freference\u002Fpackages\u002Frequirements","1.v1\u002F12.reference\u002F2.packages\u002Frequirements",{"title":243,"path":244,"stem":245},"signed-request","\u002Fv1\u002Freference\u002Fpackages\u002Fsigned-request","1.v1\u002F12.reference\u002F2.packages\u002Fsigned-request",{"title":247,"path":248,"stem":249},"storage","\u002Fv1\u002Freference\u002Fpackages\u002Fstorage","1.v1\u002F12.reference\u002F2.packages\u002Fstorage",{"title":251,"path":252,"stem":253},"suite-identity","\u002Fv1\u002Freference\u002Fpackages\u002Fsuite-identity","1.v1\u002F12.reference\u002F2.packages\u002Fsuite-identity",{"title":255,"path":256,"stem":257},"testimony","\u002Fv1\u002Freference\u002Fpackages\u002Ftestimony","1.v1\u002F12.reference\u002F2.packages\u002Ftestimony",{"title":259,"path":260,"stem":261},"testimony-qa","\u002Fv1\u002Freference\u002Fpackages\u002Ftestimony-qa","1.v1\u002F12.reference\u002F2.packages\u002Ftestimony-qa",{"title":263,"path":264,"stem":265},"trust-graph","\u002Fv1\u002Freference\u002Fpackages\u002Ftrust-graph","1.v1\u002F12.reference\u002F2.packages\u002Ftrust-graph",{"title":267,"path":268,"stem":269},"web-session","\u002Fv1\u002Freference\u002Fpackages\u002Fweb-session","1.v1\u002F12.reference\u002F2.packages\u002Fweb-session",{"title":271,"path":272,"stem":273},"webauthn-lock","\u002Fv1\u002Freference\u002Fpackages\u002Fwebauthn-lock","1.v1\u002F12.reference\u002F2.packages\u002Fwebauthn-lock",{"title":275,"path":276,"stem":277},"Apps","\u002Fv1\u002Freference\u002Fapps","1.v1\u002F12.reference\u002F3.apps",{"title":279,"path":280,"stem":281},"Services","\u002Fv1\u002Freference\u002Fservices","1.v1\u002F12.reference\u002F4.services",{"title":22,"path":283,"stem":284},"\u002Fv1\u002Freference\u002Fprotocols","1.v1\u002F12.reference\u002F5.protocols","i-lucide-library","i-lucide-book-open",{"title":288,"icon":289,"path":290,"stem":291,"children":292,"page":470},"v0 · summer 2026","i-lucide-archive","\u002Fv0","2.v0",[293,329,365,401,436,452,471],{"title":294,"path":295,"stem":296,"children":297,"icon":328},"Getting Started","\u002Fv0\u002Fgetting-started","2.v0\u002F1.getting-started\u002F1.index",[298,300,304,308,312,316,320,324],{"title":299,"path":295,"stem":296},"What is this?",{"title":301,"path":302,"stem":303},"Web, Desktop, or Mobile","\u002Fv0\u002Fgetting-started\u002Faccess-methods","2.v0\u002F1.getting-started\u002F2.access-methods",{"title":305,"path":306,"stem":307},"Before You Begin","\u002Fv0\u002Fgetting-started\u002Fbefore-you-begin","2.v0\u002F1.getting-started\u002F3.before-you-begin",{"title":309,"path":310,"stem":311},"Install the Apps","\u002Fv0\u002Fgetting-started\u002Finstall","2.v0\u002F1.getting-started\u002F4.install",{"title":313,"path":314,"stem":315},"F-Droid, Explained","\u002Fv0\u002Fgetting-started\u002Ffdroid","2.v0\u002F1.getting-started\u002F5.fdroid",{"title":317,"path":318,"stem":319},"Your First Family Tree","\u002Fv0\u002Fgetting-started\u002Fyour-first-tree","2.v0\u002F1.getting-started\u002F6.your-first-tree",{"title":321,"path":322,"stem":323},"Invite Family Members","\u002Fv0\u002Fgetting-started\u002Finvite-family","2.v0\u002F1.getting-started\u002F7.invite-family",{"title":325,"path":326,"stem":327},"Connect Family Branches","\u002Fv0\u002Fgetting-started\u002Fconnect-branches","2.v0\u002F1.getting-started\u002F8.connect-branches","i-lucide-footprints",{"title":330,"path":331,"stem":332,"children":333,"icon":45},"Abantu","\u002Fv0\u002Fabantu","2.v0\u002F2.abantu\u002F1.index",[334,336,339,342,345,349,353,357,361],{"title":335,"path":331,"stem":332},"Abantu — Community Governance",{"title":12,"path":337,"stem":338},"\u002Fv0\u002Fabantu\u002Fcommunities","2.v0\u002F2.abantu\u002F2.communities",{"title":22,"path":340,"stem":341},"\u002Fv0\u002Fabantu\u002Fprotocols","2.v0\u002F2.abantu\u002F3.protocols",{"title":34,"path":343,"stem":344},"\u002Fv0\u002Fabantu\u002Fprojects","2.v0\u002F2.abantu\u002F4.projects",{"title":346,"path":347,"stem":348},"Proposals and Roles","\u002Fv0\u002Fabantu\u002Fproposals-and-roles","2.v0\u002F2.abantu\u002F5.proposals-and-roles",{"title":350,"path":351,"stem":352},"Sync and Relay","\u002Fv0\u002Fabantu\u002Fsync-and-relay","2.v0\u002F2.abantu\u002F6.sync-and-relay",{"title":354,"path":355,"stem":356},"Identity and Devices","\u002Fv0\u002Fabantu\u002Fidentity-and-devices","2.v0\u002F2.abantu\u002F7.identity-and-devices",{"title":358,"path":359,"stem":360},"Data and Privacy","\u002Fv0\u002Fabantu\u002Fprivacy","2.v0\u002F2.abantu\u002F8.privacy",{"title":362,"path":363,"stem":364},"Plugins and Extensibility","\u002Fv0\u002Fabantu\u002Fplugins-and-extensibility","2.v0\u002F2.abantu\u002F9.plugins-and-extensibility",{"title":366,"path":367,"stem":368,"children":369,"icon":400},"Iwacu","\u002Fv0\u002Fiwacu","2.v0\u002F3.iwacu\u002F1.index",[370,372,376,380,384,388,392,396],{"title":371,"path":367,"stem":368},"Iwacu — Family Tree",{"title":373,"path":374,"stem":375},"Terminal Commands","\u002Fv0\u002Fiwacu\u002Fterminal-commands","2.v0\u002F3.iwacu\u002F2.terminal-commands",{"title":377,"path":378,"stem":379},"Manage Page and Sync","\u002Fv0\u002Fiwacu\u002Fmanage-and-sync","2.v0\u002F3.iwacu\u002F3.manage-and-sync",{"title":381,"path":382,"stem":383},"Family History and the Atlas","\u002Fv0\u002Fiwacu\u002Fhistory-and-atlas","2.v0\u002F3.iwacu\u002F4.history-and-atlas",{"title":385,"path":386,"stem":387},"Script Editor","\u002Fv0\u002Fiwacu\u002Fscript-editor","2.v0\u002F3.iwacu\u002F5.script-editor",{"title":389,"path":390,"stem":391},"Inviting and Connecting","\u002Fv0\u002Fiwacu\u002Finviting-and-connecting","2.v0\u002F3.iwacu\u002F6.inviting-and-connecting",{"title":393,"path":394,"stem":395},"Desktop App and Devices","\u002Fv0\u002Fiwacu\u002Fdesktop-and-devices","2.v0\u002F3.iwacu\u002F7.desktop-and-devices",{"title":397,"path":398,"stem":399},"Reference","\u002Fv0\u002Fiwacu\u002Freference","2.v0\u002F3.iwacu\u002F8.reference","i-lucide-trees",{"title":402,"path":403,"stem":404,"children":405,"icon":435},"Isoko","\u002Fv0\u002Fisoko","2.v0\u002F4.isoko\u002F1.index",[406,408,411,415,419,423,427,431],{"title":407,"path":403,"stem":404},"Isoko — Local Marketplace",{"title":22,"path":409,"stem":410},"\u002Fv0\u002Fisoko\u002Fprotocols","2.v0\u002F4.isoko\u002F2.protocols",{"title":412,"path":413,"stem":414},"Listings and Messaging","\u002Fv0\u002Fisoko\u002Flistings-and-messaging","2.v0\u002F4.isoko\u002F3.listings-and-messaging",{"title":416,"path":417,"stem":418},"Tracked Deliveries","\u002Fv0\u002Fisoko\u002Fdeliveries","2.v0\u002F4.isoko\u002F4.deliveries",{"title":420,"path":421,"stem":422},"Wallet and Reputation","\u002Fv0\u002Fisoko\u002Fwallet-and-reputation","2.v0\u002F4.isoko\u002F5.wallet-and-reputation",{"title":424,"path":425,"stem":426},"Market Cells","\u002Fv0\u002Fisoko\u002Fcells","2.v0\u002F4.isoko\u002F6.cells",{"title":428,"path":429,"stem":430},"Safety Scanner","\u002Fv0\u002Fisoko\u002Fsafety","2.v0\u002F4.isoko\u002F7.safety",{"title":432,"path":433,"stem":434},"Web Version, Privacy, and Limitations","\u002Fv0\u002Fisoko\u002Fweb-and-privacy","2.v0\u002F4.isoko\u002F8.web-and-privacy","i-lucide-store",{"title":437,"path":438,"stem":439,"children":440,"icon":451},"Playground","\u002Fv0\u002Fplayground","2.v0\u002F5.playground\u002F1.index",[441,443,447],{"title":442,"path":438,"stem":439},"Playground — Try It in the Browser",{"title":444,"path":445,"stem":446},"Using the Tutorials","\u002Fv0\u002Fplayground\u002Fusing-tutorials","2.v0\u002F5.playground\u002F2.using-tutorials",{"title":448,"path":449,"stem":450},"How Tutorials Are Made","\u002Fv0\u002Fplayground\u002Fbuilding-tutorials","2.v0\u002F5.playground\u002F3.building-tutorials","i-lucide-flask-conical",{"title":453,"icon":454,"path":455,"stem":456,"children":457,"page":470},"Relays","i-lucide-server","\u002Fv0\u002Frelays","2.v0\u002F6.relays",[458,462,466],{"title":459,"path":460,"stem":461},"Relays Overview","\u002Fv0\u002Frelays\u002Foverview","2.v0\u002F6.relays\u002F1.overview",{"title":463,"path":464,"stem":465},"Deploy a Relay Node","\u002Fv0\u002Frelays\u002Frelay","2.v0\u002F6.relays\u002F2.relay",{"title":467,"path":468,"stem":469},"Configure Tor Hidden Service","\u002Fv0\u002Frelays\u002Ftor","2.v0\u002F6.relays\u002F3.tor",false,{"title":472,"icon":473,"path":474,"stem":475,"children":476,"page":470},"Privacy & Security","i-lucide-shield","\u002Fv0\u002Fprivacy","2.v0\u002F7.privacy",[477,481,485],{"title":478,"path":479,"stem":480},"How Privacy Works Here","\u002Fv0\u002Fprivacy\u002Fhow-it-works","2.v0\u002F7.privacy\u002F1.how-it-works",{"title":482,"path":483,"stem":484},"Device Security","\u002Fv0\u002Fprivacy\u002Fdevice","2.v0\u002F7.privacy\u002F2.device",{"title":486,"path":487,"stem":488},"Under Pressure","\u002Fv0\u002Fprivacy\u002Fcoercion","2.v0\u002F7.privacy\u002F3.coercion",{"id":490,"title":362,"body":491,"description":1129,"extension":1130,"links":1131,"meta":1132,"navigation":649,"path":363,"seo":1133,"stem":364,"__hash__":1134},"docs\u002F2.v0\u002F2.abantu\u002F9.plugins-and-extensibility.md",{"type":492,"value":493,"toc":1121},"minimark",[494,498,502,505,510,513,516,524,528,548,560,567,571,582,758,761,765,768,968,982,986,994,1082,1089,1099,1103,1114,1117],[495,496,362],"h1",{"id":497},"plugins-and-extensibility",[499,500,501],"p",{},"Abantu has no platform owner, and neither does its code. If your community self-hosts its own instance, you can add features to it — a new kind of signed event, a new tab on the community page — without forking the whole app and without waiting on anyone else's release schedule.",[499,503,504],{},"This page is for whoever runs your instance's build, not for everyday members — you won't find a settings toggle here, just a real, working convention with a reference implementation you can copy.",[506,507,509],"h2",{"id":508},"why-this-needs-to-be-a-real-mechanism","Why this needs to be a real mechanism",[499,511,512],{},"Abantu's community state is derived by replaying a signed event log through a fixed set of reducers — one per event type. Historically that set was a single hardcoded switch statement: adding a new kind of event meant editing that file directly, which put every extension behind a fork of the core app.",[499,514,515],{},"The same was true of the community page's tabs (Proposals, Roles, Protocols, Projects, Members) — five hardcoded entries with no way to add a sixth without editing the page itself.",[499,517,518,519,523],{},"Both are now ",[520,521,522],"strong",{},"registries"," instead: a plugin calls a function to add its own event type or its own tab, at your build's startup, and core never has to know the plugin exists.",[506,525,527],{"id":526},"the-two-extension-points","The two extension points",[529,530,533],"callout",{"color":531,"icon":532},"sky","i-lucide-database",[499,534,535,538,539,543,544,547],{},[520,536,537],{},"A new event type."," Call ",[540,541,542],"code",{},"registerEventReducer(type, reducer)"," to teach the community log a signed event type of your own. Your reducer gets ",[540,545,546],{},"state.pluginState"," — a plain object reserved for plugins, separate from everything core reads and writes — to store whatever your event introduces.",[529,549,551],{"color":531,"icon":550},"i-lucide-layout-panel-top",[499,552,553,538,556,559],{},[520,554,555],{},"A new community-page tab.",[540,557,558],{},"registerCommunityTab({ id, label, icon, component })"," to add a tab alongside the five built-in ones. Your component receives the community's id as a prop, and can read\u002Fwrite state through an injected context — see below — without needing to import anything from inside the abantu app itself.",[499,561,562,563,566],{},"Both live in ",[540,564,565],{},"@paramira\u002Fcommunity-plugin-api",", a small package with no abantu-specific code in it — the only thing a plugin package and abantu's own app share.",[506,568,570],{"id":569},"reading-and-writing-state-from-a-tab","Reading and writing state from a tab",[499,572,573,574,577,578,581],{},"A plugin's tab component is bundled into abantu's build, but it isn't part of the app's own source tree, so it can't reach abantu's internal composables (",[540,575,576],{},"~\u002Fcomposables\u002FuseCommunity",", and so on) the normal way. Instead, abantu provides a small context object your component reads with Vue's ",[540,579,580],{},"inject()",":",[583,584,589],"pre",{"className":585,"code":586,"language":587,"meta":588,"style":588},"language-js shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { inject } from 'vue'\nimport { PLUGIN_CONTEXT_KEY } from '@paramira\u002Fcommunity-plugin-api'\n\nconst ctx = inject(PLUGIN_CONTEXT_KEY)\n\n\u002F\u002F Read: current derived state for a community, including your pluginState\nconst state = ctx.getState(communityId)\n\n\u002F\u002F Write: append a signed event of a type you've registered a reducer for\nawait ctx.appendCustomEvent(communityId, 'your:event-type', { ...payload })\n","js","",[540,590,591,624,644,651,670,675,682,704,709,715],{"__ignoreMap":588},[592,593,596,600,604,608,611,614,617,621],"span",{"class":594,"line":595},"line",1,[592,597,599],{"class":598},"s7zQu","import",[592,601,603],{"class":602},"sMK4o"," {",[592,605,607],{"class":606},"sTEyZ"," inject",[592,609,610],{"class":602}," }",[592,612,613],{"class":598}," from",[592,615,616],{"class":602}," '",[592,618,620],{"class":619},"sfazB","vue",[592,622,623],{"class":602},"'\n",[592,625,627,629,631,634,636,638,640,642],{"class":594,"line":626},2,[592,628,599],{"class":598},[592,630,603],{"class":602},[592,632,633],{"class":606}," PLUGIN_CONTEXT_KEY",[592,635,610],{"class":602},[592,637,613],{"class":598},[592,639,616],{"class":602},[592,641,565],{"class":619},[592,643,623],{"class":602},[592,645,647],{"class":594,"line":646},3,[592,648,650],{"emptyLinePlaceholder":649},true,"\n",[592,652,654,658,661,664,667],{"class":594,"line":653},4,[592,655,657],{"class":656},"spNyl","const",[592,659,660],{"class":606}," ctx ",[592,662,663],{"class":602},"=",[592,665,607],{"class":666},"s2Zo4",[592,668,669],{"class":606},"(PLUGIN_CONTEXT_KEY)\n",[592,671,673],{"class":594,"line":672},5,[592,674,650],{"emptyLinePlaceholder":649},[592,676,678],{"class":594,"line":677},6,[592,679,681],{"class":680},"sHwdD","\u002F\u002F Read: current derived state for a community, including your pluginState\n",[592,683,685,687,690,692,695,698,701],{"class":594,"line":684},7,[592,686,657],{"class":656},[592,688,689],{"class":606}," state ",[592,691,663],{"class":602},[592,693,694],{"class":606}," ctx",[592,696,697],{"class":602},".",[592,699,700],{"class":666},"getState",[592,702,703],{"class":606},"(communityId)\n",[592,705,707],{"class":594,"line":706},8,[592,708,650],{"emptyLinePlaceholder":649},[592,710,712],{"class":594,"line":711},9,[592,713,714],{"class":680},"\u002F\u002F Write: append a signed event of a type you've registered a reducer for\n",[592,716,718,721,723,725,728,731,734,736,739,742,744,746,749,752,755],{"class":594,"line":717},10,[592,719,720],{"class":598},"await",[592,722,694],{"class":606},[592,724,697],{"class":602},[592,726,727],{"class":666},"appendCustomEvent",[592,729,730],{"class":606},"(communityId",[592,732,733],{"class":602},",",[592,735,616],{"class":602},[592,737,738],{"class":619},"your:event-type",[592,740,741],{"class":602},"'",[592,743,733],{"class":602},[592,745,603],{"class":602},[592,747,748],{"class":602}," ...",[592,750,751],{"class":606},"payload ",[592,753,754],{"class":602},"}",[592,756,757],{"class":606},")\n",[499,759,760],{},"That's the entire surface. No other abantu internals are reachable from a plugin, on purpose — the state your reducer writes and the events you append are the only channel.",[506,762,764],{"id":763},"versioning","Versioning",[499,766,767],{},"A plugin declares which version of the extension contract it was built against:",[583,769,771],{"className":585,"code":770,"language":587,"meta":588,"style":588},"const plugin = {\n  id: 'your-plugin-id',\n  name: 'Your Plugin',\n  version: '0.1.0',\n  apiVersion: 1, \u002F\u002F must match @paramira\u002Fcommunity-plugin-api's PLUGIN_API_VERSION\n  install() {\n    registerEventReducer('your:event-type', yourReducer)\n    registerCommunityTab({ id: 'your-tab', label: 'Your Tab', icon: 'i-lucide-star', component: YourTab })\n  },\n}\nexport default plugin\n",[540,772,773,785,803,819,835,851,861,882,946,951,956],{"__ignoreMap":588},[592,774,775,777,780,782],{"class":594,"line":595},[592,776,657],{"class":656},[592,778,779],{"class":606}," plugin ",[592,781,663],{"class":602},[592,783,784],{"class":602}," {\n",[592,786,787,791,793,795,798,800],{"class":594,"line":626},[592,788,790],{"class":789},"swJcz","  id",[592,792,581],{"class":602},[592,794,616],{"class":602},[592,796,797],{"class":619},"your-plugin-id",[592,799,741],{"class":602},[592,801,802],{"class":602},",\n",[592,804,805,808,810,812,815,817],{"class":594,"line":646},[592,806,807],{"class":789},"  name",[592,809,581],{"class":602},[592,811,616],{"class":602},[592,813,814],{"class":619},"Your Plugin",[592,816,741],{"class":602},[592,818,802],{"class":602},[592,820,821,824,826,828,831,833],{"class":594,"line":653},[592,822,823],{"class":789},"  version",[592,825,581],{"class":602},[592,827,616],{"class":602},[592,829,830],{"class":619},"0.1.0",[592,832,741],{"class":602},[592,834,802],{"class":602},[592,836,837,840,842,846,848],{"class":594,"line":672},[592,838,839],{"class":789},"  apiVersion",[592,841,581],{"class":602},[592,843,845],{"class":844},"sbssI"," 1",[592,847,733],{"class":602},[592,849,850],{"class":680}," \u002F\u002F must match @paramira\u002Fcommunity-plugin-api's PLUGIN_API_VERSION\n",[592,852,853,856,859],{"class":594,"line":677},[592,854,855],{"class":789},"  install",[592,857,858],{"class":602},"()",[592,860,784],{"class":602},[592,862,863,866,869,871,873,875,877,880],{"class":594,"line":684},[592,864,865],{"class":666},"    registerEventReducer",[592,867,868],{"class":789},"(",[592,870,741],{"class":602},[592,872,738],{"class":619},[592,874,741],{"class":602},[592,876,733],{"class":602},[592,878,879],{"class":606}," yourReducer",[592,881,757],{"class":789},[592,883,884,887,889,892,895,897,899,902,904,906,909,911,913,916,918,920,923,925,927,930,932,934,937,939,942,944],{"class":594,"line":706},[592,885,886],{"class":666},"    registerCommunityTab",[592,888,868],{"class":789},[592,890,891],{"class":602},"{",[592,893,894],{"class":789}," id",[592,896,581],{"class":602},[592,898,616],{"class":602},[592,900,901],{"class":619},"your-tab",[592,903,741],{"class":602},[592,905,733],{"class":602},[592,907,908],{"class":789}," label",[592,910,581],{"class":602},[592,912,616],{"class":602},[592,914,915],{"class":619},"Your Tab",[592,917,741],{"class":602},[592,919,733],{"class":602},[592,921,922],{"class":789}," icon",[592,924,581],{"class":602},[592,926,616],{"class":602},[592,928,929],{"class":619},"i-lucide-star",[592,931,741],{"class":602},[592,933,733],{"class":602},[592,935,936],{"class":789}," component",[592,938,581],{"class":602},[592,940,941],{"class":606}," YourTab",[592,943,610],{"class":602},[592,945,757],{"class":789},[592,947,948],{"class":594,"line":711},[592,949,950],{"class":602},"  },\n",[592,952,953],{"class":594,"line":717},[592,954,955],{"class":602},"}\n",[592,957,959,962,965],{"class":594,"line":958},11,[592,960,961],{"class":598},"export",[592,963,964],{"class":598}," default",[592,966,967],{"class":606}," plugin\n",[499,969,970,973,974,977,978,981],{},[540,971,972],{},"installPlugin(plugin)"," checks ",[540,975,976],{},"apiVersion"," before ever calling ",[540,979,980],{},"install()"," — a plugin built against a contract your build has moved past is skipped with a console warning instead of running against assumptions that no longer hold.",[506,983,985],{"id":984},"installing-a-plugin","Installing a plugin",[499,987,988,989,993],{},"Everything above is how you ",[990,991,992],"em",{},"write"," a plugin. Installing one into your own build is one file:",[583,995,999],{"className":996,"code":997,"language":998,"meta":588,"style":588},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F apps\u002Fabantu\u002Fapp\u002Fplugins\u002Fcommunity-plugins.client.ts\nimport { installPlugin } from '@paramira\u002Fcommunity-plugin-api'\nimport yourPlugin from '@your-org\u002Fyour-plugin'\n\nexport default defineNuxtPlugin(() => {\n  installPlugin(yourPlugin)\n})\n","ts",[540,1000,1001,1006,1025,1042,1046,1064,1076],{"__ignoreMap":588},[592,1002,1003],{"class":594,"line":595},[592,1004,1005],{"class":680},"\u002F\u002F apps\u002Fabantu\u002Fapp\u002Fplugins\u002Fcommunity-plugins.client.ts\n",[592,1007,1008,1010,1012,1015,1017,1019,1021,1023],{"class":594,"line":626},[592,1009,599],{"class":598},[592,1011,603],{"class":602},[592,1013,1014],{"class":606}," installPlugin",[592,1016,610],{"class":602},[592,1018,613],{"class":598},[592,1020,616],{"class":602},[592,1022,565],{"class":619},[592,1024,623],{"class":602},[592,1026,1027,1029,1032,1035,1037,1040],{"class":594,"line":646},[592,1028,599],{"class":598},[592,1030,1031],{"class":606}," yourPlugin ",[592,1033,1034],{"class":598},"from",[592,1036,616],{"class":602},[592,1038,1039],{"class":619},"@your-org\u002Fyour-plugin",[592,1041,623],{"class":602},[592,1043,1044],{"class":594,"line":653},[592,1045,650],{"emptyLinePlaceholder":649},[592,1047,1048,1050,1052,1055,1057,1059,1062],{"class":594,"line":672},[592,1049,961],{"class":598},[592,1051,964],{"class":598},[592,1053,1054],{"class":666}," defineNuxtPlugin",[592,1056,868],{"class":606},[592,1058,858],{"class":602},[592,1060,1061],{"class":656}," =>",[592,1063,784],{"class":602},[592,1065,1066,1069,1071,1074],{"class":594,"line":677},[592,1067,1068],{"class":666},"  installPlugin",[592,1070,868],{"class":789},[592,1072,1073],{"class":606},"yourPlugin",[592,1075,757],{"class":789},[592,1077,1078,1080],{"class":594,"line":684},[592,1079,754],{"class":602},[592,1081,757],{"class":606},[499,1083,1084,1085,1088],{},"Add the plugin package as a dependency, add the import and the ",[540,1086,1087],{},"installPlugin()"," call, rebuild. Abantu ships as a static site, so a plugin takes effect at your next build — not at runtime, and not for anyone else's instance.",[529,1090,1092],{"color":531,"icon":1091},"i-lucide-info",[499,1093,1094,1095,1098],{},"There is no registry or marketplace of installable plugins yet — for now this is a source-level convention between your build and a plugin package you (or someone you trust) wrote, whether that's a workspace package or a private npm package. ",[540,1096,1097],{},"packages\u002Fexample-community-plugin"," in the paramira monorepo is a complete, tested reference implementation — a \"Badges\" tab and the event type behind it — built exactly to this contract. Copying its shape is the fastest way to build your own.",[506,1100,1102],{"id":1101},"what-this-doesnt-cover","What this doesn't cover",[499,1104,1105,1106,1109,1110,1113],{},"This convention is specific to ",[520,1107,1108],{},"abantu's own app"," — its community state and its community page. Isoko (the marketplace app) has a comparable extension point for its own scenario automation (see its ",[540,1111,1112],{},"registerScenarioAction","), but it's a separate mechanism in a separate app, since isoko and abantu are different running processes on different devices. A plugin that wants to extend both writes two small packages, one per app, not one that reaches across.",[499,1115,1116],{},"This is also a build-time convention, not a runtime one — a plugin can't be toggled on for a live instance without a rebuild and redeploy. That's a deliberate trade-off for a statically generated app with no server of its own; a dynamic, install-without-rebuilding story is a bigger undertaking left for later, if it ever turns out to be worth the added complexity.",[1118,1119,1120],"style",{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":588,"searchDepth":595,"depth":626,"links":1122},[1123,1124,1125,1126,1127,1128],{"id":508,"depth":626,"text":509},{"id":526,"depth":626,"text":527},{"id":569,"depth":626,"text":570},{"id":763,"depth":626,"text":764},{"id":984,"depth":626,"text":985},{"id":1101,"depth":626,"text":1102},"How a self-hosted abantu instance can add its own features — a real, working extension point, not just a roadmap idea.","md",null,{},{"title":362,"description":1129},"UfWxTj6VTNuYk1nhK3BfWL4o7Nf6zcx-BgzVjfNokqA",[1136,1138],{"title":358,"path":359,"stem":360,"description":1137,"children":-1},"What the relay knows, what other members know, and what nobody can see — in abantu specifically.",{"title":371,"path":367,"stem":368,"description":1139,"children":-1},"What iwacu is, and the three areas of the screen you'll use every time.",1791986797333]