← All extensions

Interoperability · Root

AGM Encryption

by Mango

AGM v1-compatible AES-256-GCM encryption with authenticated per-message nonces and replay protection.

Install in Mango 3.116 KB · Mango 3.1
Version
0.1.9
Runs as
Server Root
Grove API
1
Updated
2026-09-06

Requested access

6 permissions

  • Add slash commandsRegisters its own commands and can show native command-result cards.
  • Filter received messagesCan transform or hide incoming message text before Mango displays it.
  • Filter sent messagesCan transform or block outgoing message text before it reaches IRC.
  • messages.protectA Grove permission declared by this extension.
  • Keep local dataStores extension data on this device in its own isolated space.
  • Keep secrets in KeychainStores private values in a Keychain namespace reserved for this extension.

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

  1. Open the same channel in Mango and the other client, or open a private query with the other device’s nickname on each client.
  2. Run /agm gen in Mango.
  3. Copy the Base64 key from the card and import it for that target in the other client.
  4. Compare the XXXX-XXXX safety number out of band.
  5. 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 clearconflicts before 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 /me path 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": []
}