Interoperability · Root
AGM Encryption
AGM v1-compatible AES-256-GCM encryption with authenticated per-message nonces and replay protection.
- Version
- 0.1.9
- Runs as
- Server Root
- Grove API
- 1
- Updated
- 2026-09-06
Requested access
6 permissions
Network access
No internet access
Its manifest declares no external hosts. Grove’s sandbox blocks network requests outside the package.
From the extension
README
AGM Encryption — a Grove Root
AGM adds AGM v1-compatible +AGM encryption to Mango. It uses a shared
32-byte key per channel or private conversation and sends standard AGM v1
AES-256-GCM payloads that other AGM implementations can read.
Quick start with another AGM client
- Open the same channel in Mango and the other client, or open a private query with the other device’s nickname on each client.
- Run
/agm genin Mango. - Copy the Base64 key from the card and import it for that target in the other client.
- Compare the
XXXX-XXXXsafety number out of band. - Send an ordinary message from either client.
If the other client generated the key, copy its Base64 value and run
/agm set <base64-key> in the matching Mango conversation instead. Target
names are per side: in a query, each client assigns the key to the other
person’s nickname.
Commands
Run these in the channel or query whose key you want to manage:
/agm gen
/agm set <base64-key>
/agm export
/agm clear
/agm clearconflicts
/agm status
/agm list
The target index is stored in the Grove WebKit store. Actual key bytes are
stored in Keychain through storage.secure. /agm list and /agm status
never reveal keys; /agm export deliberately reveals the current key so it can
be transferred to another client.
Interoperability and behavior
- Wire format:
+AGM <base64(version || nonce || ciphertext || tag)>. - Cipher: AES-256-GCM with a fresh random 96-bit nonce per message.
- AAD: lowercased channel name, or the UTF-8-sorted pair of lowercased nicknames joined with NUL for a query.
- Key: exactly 32 random bytes, imported/exported as standard RFC 4648 Base64.
- Safety number: the AGM-compatible first 40 bits of
SHA-256(0x00 || key), rendered with its unambiguous Base32 alphabet. - Long messages are split at UTF-8 scalar boundaries before encryption. Every chunk has a distinct nonce and is sent atomically by Mango.
- Authenticated live nonces are remembered in a bounded 2,048-entry replay cache. Historical and local transcript replay remain decryptable.
- Query keys follow authoritative nickname changes. Previous authenticated
conversation identities are retained so pre-rename cached history still
decrypts. A rename colliding with another saved key disables both entries
until the user explicitly chooses or clears the key for the new nickname.
Legacy unlinked conflicts from 0.1.3 require
/agm clearconflictsbefore their keys can be configured again. - Keychain changes are guarded by a durable recovery marker. If a key update is interrupted, AGM stays fail closed across app restarts until the affected key is set again or cleared.
- Channel keys fail closed for outbound messages, but they do not require every participant to use AGM. Plain inbound channel messages remain visible without an AGM indicator; only successfully authenticated frames receive the AGM lock.
- Incoming encrypted ACTION (
/me) text is decrypted. Mango’s current outbound/mepath does not pass through Grove filters, so outgoing ACTION encryption is not included in this release.
Malformed, tampered, wrong-key, or cross-conversation payloads stay visible as
raw +AGM text with an error indicator. A repeated authenticated live nonce is
dropped. With no key configured for a target, AGM payloads pass through
unchanged.
Diagnostics
The Grove console records key lifecycle events and live encryption/decryption activity, including the target, conversation type, message kind, delivery type, and encrypted frame count. It also reports authentication failures and replay rejections. Logs never include message plaintext, keys, or ciphertext.
Security warning
AGM is authenticated pre-shared-key encryption and a substantial improvement over FiSH, but it is not OTR or Signal. It has no key exchange, peer identity, deniability, forward secrecy, or post-compromise security. Anyone who obtains the static key can decrypt recorded traffic encrypted with that key. Share keys through a trusted private channel, compare safety numbers out of band, rotate keys when membership changes, and keep IRC transport TLS enabled.
This implementation is independently written against the public AGM v1 wire-format documentation. Its bundled cryptographic primitives come from the MIT-licensed Noble Ciphers and Noble Hashes projects.
Development
npm install
npm test
npm run package
manifest.jsonapp.mango.grove.agm
{
"grove": 1,
"id": "app.mango.grove.agm",
"name": "AGM Encryption",
"version": "0.1.9",
"description": "AGM v1-compatible AES-256-GCM encryption with authenticated per-message nonces and replay protection.",
"author": {
"name": "Mango",
"url": "https://mangoirc.chat"
},
"readme": "README.md",
"entry": "index.html",
"presentation": {
"surfaces": [
"headless"
],
"scope": "server"
},
"capabilities": [
"commands.register",
"messages.filter.inbound",
"messages.filter.outbound",
"messages.protect",
"storage",
"storage.secure"
],
"hostPermissions": []
}