{"owner":"haraka","repo":"Haraka","hasSkills":true,"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md",".github/copilot-instructions.md"],"skills":{"AGENTS.md":"<!-- agents-version: 2 -->\n\n# AGENTS.md\n\nShared instructions for every coding agent in this repo. The tool-specific files\n(`CLAUDE.md`, `GEMINI.md`, `.github/copilot-instructions.md`) defer to this one.\nIf a package-level instruction file exists, it is authoritative for that package.\n\n## Repository model\n\n- This file is shared verbatim across every Haraka repository, so it describes the whole family — not just the repo you are in. Each repo is an independent npm package with its own `package.json`, tests, and version history; there is no root workspace or shared test runner. Run every command from the package root.\n- Repos are worked on both standalone and as sibling checkouts in a combined tree (`Haraka/`, `plugin/<name>/`, …). Don't assume sibling packages are present on disk.\n- The family:\n  - `Haraka` — core SMTP server (has `./run_tests`).\n  - `haraka-plugin-<name>` — optional plugins; may depend on each other (e.g. bounce → spf).\n  - `haraka-config` — config loader with hot-reload.\n  - `haraka-results`, `haraka-notes` — per-connection result / note tracking.\n  - `haraka-net-utils`, `haraka-utils`, `haraka-constants`, `haraka-dsn`, `haraka-tld`, `haraka-message-stream` — shared libraries.\n  - `@haraka/email-address` — the RFC 5321/5322 parser (supersedes the deprecated `address-rfc282x`).\n  - `@haraka/eslint-config`, `haraka-test-fixtures`.\n\n## Working agreement\n\n- Do only what was asked. When you spot an adjacent bug or smell, surface it and ask before expanding scope — don't silently refactor, but don't ignore it either.\n- Preserve compatibility; break it only for an explicit, stated reason.\n- For protocol behavior, identify the relevant RFC and verify conformance against the existing implementation.\n\n## Source control\n\n- Never run history- or remote-mutating commands (`git commit`, `git push`, `git tag`, `gh pr create`) unless explicitly asked. Stage diffs; the human reviews, commits, and pushes.\n- Propose a commit message in Conventional Commit format, imperative mood.\n- Update `CHANGELOG.md` under `### Unreleased`: one terse clause per change, least markup. Rationale belongs in the code or PR, not the bullet.\n\n## Coding standards\n\n- Target current Node LTS; prefer ES2024 over legacy patterns.\n- Existing code is CommonJS (`require`/`exports`) — match it. New modules should use ESM with CJS interop (see `@haraka/email-address`).\n- Add `node:` prefixes to built-in requires in any file you touch (`require('fs')` → `require('node:fs')`).\n- Prefer: promise APIs (`fs/promises`), `for...of`/`for...in` over `forEach`, `node:readline` for line parsing, template literals over concatenation, `true`/`false` over `1`/`0`, and guard-style early returns.\n- Remove commented-out code (it lives in git history). `npm run qlty` must pass without warnings.\n\n## Comments\n\n- Prefer self-documenting code: a better name beats a comment.\n- Keep only WHY comments — a hidden constraint, an invariant, a workaround for a specific bug, or an RFC citation that explains otherwise-surprising behavior.\n- Delete WHAT comments that restate the code, and comments that narrate history or audit findings. If a rename makes a comment redundant, delete it rather than updating it.\n\n## Haraka plugins\n\n- Full hook/API reference: `docs/Plugins.md` in the `Haraka` core repo.\n- A plugin is an npm package: `index.js` (`exports.register` + hook handlers), `config/` (default `.ini`/`.json`/`.yaml`), `test/`, `README.md`.\n- Register hooks in `exports.register` with `this.register_hook('phase', 'method'[, priority])`.\n- Hook handlers take `(next, connection)` (rcpt hooks also take `rcpt`) and must call `next` exactly once. Gate early — return `next()` on missing transaction, disabled config, or skip conditions. Signal a verdict with `next(DENY|DENYSOFT|OK, msg)`; `DENY`/`OK`/etc. are plugin-scope globals (no import).\n- Results: `connection.transaction.results.add(this, { pass|fail|skip|msg|err, emit })`; query with `results.has(plugin, list, search)`. `emit: true` already logs the collated line — don't also `loginfo`/`logerror` the same thing. results.add(this, {err}) always logs.\n- Config loads via `config.get` with a hot-reload callback; declare every boolean or it stays a string and `=== true/false` silently fails:\n  ```js\n  this.cfg = this.config.get('name.ini', { booleans: ['+a.b', '-c.d'] }, () => this.load())\n  ```\n- Keep handlers thin. Push pure decision logic and I/O into `lib/*.js` as pure functions that return a verdict/value; the handler just maps that to `results.add` + `next`. For external I/O (DNS, network), expose an injectable seam — a swappable function whose default is the real implementation — so tests run without mocks.\n- If you add files outside `index.js` (e.g. a `lib/` dir), add them to `package.json` `files` so they publish.\n\n## Testing\n\n- Test real behavior and observable outcomes — `results`, return codes, emitted headers, side effects — not how a function was called. Asserting call shape (`calledWith`, arity, call counts) tests the test and hides signature drift.\n- Mocks/stubs are a smell. Prefer real inputs; when you must isolate a dependency, inject a seam and assert the outcome. Never leave a stub that neuters the path under test — that yields green tests proving nothing.\n- For bug fixes, add a failing test first, then fix.\n- Every feature ships with meaningful tests. A `.skip` is a coverage hole: fix it or delete it.\n- Use `node:test` and `node:assert/strict` for new tests and Mocha migrations. Plugin tests use `haraka-test-fixtures` (`makePlugin`, `makeConnection`, `callHook`).\n- Run the package's `lint`, `prettier`, and `format` before handoff.\n\n## Commands (run inside the target package)\n\n- Test: `npm test`. Single file: `node --test test/path/to/file.js`.\n- Haraka core repo only: `./run_tests [test/plugins/foo.js]`.\n- Coverage: `npm run test:coverage`; lcov: `npm run test:coverage:lcov`. Keep coverage at/above ~90%.\n- If coverage output includes non-source files (e.g. `package.json`, `test/*`), scope it with `--test-coverage-include` (preferred when the list is short) or `--test-coverage-exclude`.\n- Lint/format: `npm run lint` / `prettier` / `format`. Version drift: `npm run versions[:fix]`.\n\n## Package script parity\n\n- node:test packages should expose `test`, `test:coverage`, `test:coverage:lcov`, `lint`, `prettier`, `format` with matching shapes across siblings. Standardize on node:test coverage (not c8); add the canonical scripts when touching a package that lacks them:\n  ```jsonc\n  \"test:coverage\": \"node --test --experimental-test-coverage\",\n  \"test:coverage:lcov\": \"mkdir -p coverage && node --test --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=coverage/lcov.info\"\n  ```\n\n## Repo badges\n\n- Code climate is deprecated. Update with qlty.sh instead.\n- The canonical format for badges should be:\n  - Top of README.md:\n    - [![Test][ci-img]][ci-url] [![Cover][cov-img]][cov-url] [![Qlty][qlty-img]][qlty-url]\n  - Bottom of README.md:\n    - [ci-img]: https://github.com/haraka/<name>/actions/workflows/ci.yml/badge.svg\n    - [ci-url]: https://github.com/haraka/<name>/actions/workflows/ci.yml\n    - [cov-img]: https://codecov.io/github/haraka/<name>/coverage.svg\n    - [cov-url]: https://codecov.io/github/haraka/<name>\n    - [qlty-img]: https://qlty.sh/gh/haraka/projects/<name>/maintainability.svg\n    - [qlty-url]: https://qlty.sh/gh/haraka/projects/<name>\n",".github/copilot-instructions.md":"<!-- agents-version: 2 -->\n\n# AGENTS.md\n\nShared instructions for every coding agent in this repo. The tool-specific files\n(`CLAUDE.md`, `GEMINI.md`, `.github/copilot-instructions.md`) defer to this one.\nIf a package-level instruction file exists, it is authoritative for that package.\n\n## Repository model\n\n- This file is shared verbatim across every Haraka repository, so it describes the whole family — not just the repo you are in. Each repo is an independent npm package with its own `package.json`, tests, and version history; there is no root workspace or shared test runner. Run every command from the package root.\n- Repos are worked on both standalone and as sibling checkouts in a combined tree (`Haraka/`, `plugin/<name>/`, …). Don't assume sibling packages are present on disk.\n- The family:\n  - `Haraka` — core SMTP server (has `./run_tests`).\n  - `haraka-plugin-<name>` — optional plugins; may depend on each other (e.g. bounce → spf).\n  - `haraka-config` — config loader with hot-reload.\n  - `haraka-results`, `haraka-notes` — per-connection result / note tracking.\n  - `haraka-net-utils`, `haraka-utils`, `haraka-constants`, `haraka-dsn`, `haraka-tld`, `haraka-message-stream` — shared libraries.\n  - `@haraka/email-address` — the RFC 5321/5322 parser (supersedes the deprecated `address-rfc282x`).\n  - `@haraka/eslint-config`, `haraka-test-fixtures`.\n\n## Working agreement\n\n- Do only what was asked. When you spot an adjacent bug or smell, surface it and ask before expanding scope — don't silently refactor, but don't ignore it either.\n- Preserve compatibility; break it only for an explicit, stated reason.\n- For protocol behavior, identify the relevant RFC and verify conformance against the existing implementation.\n\n## Source control\n\n- Never run history- or remote-mutating commands (`git commit`, `git push`, `git tag`, `gh pr create`) unless explicitly asked. Stage diffs; the human reviews, commits, and pushes.\n- Propose a commit message in Conventional Commit format, imperative mood.\n- Update `CHANGELOG.md` under `### Unreleased`: one terse clause per change, least markup. Rationale belongs in the code or PR, not the bullet.\n\n## Coding standards\n\n- Target current Node LTS; prefer ES2024 over legacy patterns.\n- Existing code is CommonJS (`require`/`exports`) — match it. New modules should use ESM with CJS interop (see `@haraka/email-address`).\n- Add `node:` prefixes to built-in requires in any file you touch (`require('fs')` → `require('node:fs')`).\n- Prefer: promise APIs (`fs/promises`), `for...of`/`for...in` over `forEach`, `node:readline` for line parsing, template literals over concatenation, `true`/`false` over `1`/`0`, and guard-style early returns.\n- Remove commented-out code (it lives in git history). `npm run qlty` must pass without warnings.\n\n## Comments\n\n- Prefer self-documenting code: a better name beats a comment.\n- Keep only WHY comments — a hidden constraint, an invariant, a workaround for a specific bug, or an RFC citation that explains otherwise-surprising behavior.\n- Delete WHAT comments that restate the code, and comments that narrate history or audit findings. If a rename makes a comment redundant, delete it rather than updating it.\n\n## Haraka plugins\n\n- Full hook/API reference: `docs/Plugins.md` in the `Haraka` core repo.\n- A plugin is an npm package: `index.js` (`exports.register` + hook handlers), `config/` (default `.ini`/`.json`/`.yaml`), `test/`, `README.md`.\n- Register hooks in `exports.register` with `this.register_hook('phase', 'method'[, priority])`.\n- Hook handlers take `(next, connection)` (rcpt hooks also take `rcpt`) and must call `next` exactly once. Gate early — return `next()` on missing transaction, disabled config, or skip conditions. Signal a verdict with `next(DENY|DENYSOFT|OK, msg)`; `DENY`/`OK`/etc. are plugin-scope globals (no import).\n- Results: `connection.transaction.results.add(this, { pass|fail|skip|msg|err, emit })`; query with `results.has(plugin, list, search)`. `emit: true` already logs the collated line — don't also `loginfo`/`logerror` the same thing. results.add(this, {err}) always logs.\n- Config loads via `config.get` with a hot-reload callback; declare every boolean or it stays a string and `=== true/false` silently fails:\n  ```js\n  this.cfg = this.config.get('name.ini', { booleans: ['+a.b', '-c.d'] }, () => this.load())\n  ```\n- Keep handlers thin. Push pure decision logic and I/O into `lib/*.js` as pure functions that return a verdict/value; the handler just maps that to `results.add` + `next`. For external I/O (DNS, network), expose an injectable seam — a swappable function whose default is the real implementation — so tests run without mocks.\n- If you add files outside `index.js` (e.g. a `lib/` dir), add them to `package.json` `files` so they publish.\n\n## Testing\n\n- Test real behavior and observable outcomes — `results`, return codes, emitted headers, side effects — not how a function was called. Asserting call shape (`calledWith`, arity, call counts) tests the test and hides signature drift.\n- Mocks/stubs are a smell. Prefer real inputs; when you must isolate a dependency, inject a seam and assert the outcome. Never leave a stub that neuters the path under test — that yields green tests proving nothing.\n- For bug fixes, add a failing test first, then fix.\n- Every feature ships with meaningful tests. A `.skip` is a coverage hole: fix it or delete it.\n- Use `node:test` and `node:assert/strict` for new tests and Mocha migrations. Plugin tests use `haraka-test-fixtures` (`makePlugin`, `makeConnection`, `callHook`).\n- Run the package's `lint`, `prettier`, and `format` before handoff.\n\n## Commands (run inside the target package)\n\n- Test: `npm test`. Single file: `node --test test/path/to/file.js`.\n- Haraka core repo only: `./run_tests [test/plugins/foo.js]`.\n- Coverage: `npm run test:coverage`; lcov: `npm run test:coverage:lcov`. Keep coverage at/above ~90%.\n- If coverage output includes non-source files (e.g. `package.json`, `test/*`), scope it with `--test-coverage-include` (preferred when the list is short) or `--test-coverage-exclude`.\n- Lint/format: `npm run lint` / `prettier` / `format`. Version drift: `npm run versions[:fix]`.\n\n## Package script parity\n\n- node:test packages should expose `test`, `test:coverage`, `test:coverage:lcov`, `lint`, `prettier`, `format` with matching shapes across siblings. Standardize on node:test coverage (not c8); add the canonical scripts when touching a package that lacks them:\n  ```jsonc\n  \"test:coverage\": \"node --test --experimental-test-coverage\",\n  \"test:coverage:lcov\": \"mkdir -p coverage && node --test --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=coverage/lcov.info\"\n  ```\n\n## Repo badges\n\n- Code climate is deprecated. Update with qlty.sh instead.\n- The canonical format for badges should be:\n  - Top of README.md:\n    - [![Test][ci-img]][ci-url] [![Cover][cov-img]][cov-url] [![Qlty][qlty-img]][qlty-url]\n  - Bottom of README.md:\n    - [ci-img]: https://github.com/haraka/<name>/actions/workflows/ci.yml/badge.svg\n    - [ci-url]: https://github.com/haraka/<name>/actions/workflows/ci.yml\n    - [cov-img]: https://codecov.io/github/haraka/<name>/coverage.svg\n    - [cov-url]: https://codecov.io/github/haraka/<name>\n    - [qlty-img]: https://qlty.sh/gh/haraka/projects/<name>/maintainability.svg\n    - [qlty-url]: https://qlty.sh/gh/haraka/projects/<name>\n"},"files":{"AGENTS.md":"<!-- agents-version: 2 -->\n\n# AGENTS.md\n\nShared instructions for every coding agent in this repo. The tool-specific files\n(`CLAUDE.md`, `GEMINI.md`, `.github/copilot-instructions.md`) defer to this one.\nIf a package-level instruction file exists, it is authoritative for that package.\n\n## Repository model\n\n- This file is shared verbatim across every Haraka repository, so it describes the whole family — not just the repo you are in. Each repo is an independent npm package with its own `package.json`, tests, and version history; there is no root workspace or shared test runner. Run every command from the package root.\n- Repos are worked on both standalone and as sibling checkouts in a combined tree (`Haraka/`, `plugin/<name>/`, …). Don't assume sibling packages are present on disk.\n- The family:\n  - `Haraka` — core SMTP server (has `./run_tests`).\n  - `haraka-plugin-<name>` — optional plugins; may depend on each other (e.g. bounce → spf).\n  - `haraka-config` — config loader with hot-reload.\n  - `haraka-results`, `haraka-notes` — per-connection result / note tracking.\n  - `haraka-net-utils`, `haraka-utils`, `haraka-constants`, `haraka-dsn`, `haraka-tld`, `haraka-message-stream` — shared libraries.\n  - `@haraka/email-address` — the RFC 5321/5322 parser (supersedes the deprecated `address-rfc282x`).\n  - `@haraka/eslint-config`, `haraka-test-fixtures`.\n\n## Working agreement\n\n- Do only what was asked. When you spot an adjacent bug or smell, surface it and ask before expanding scope — don't silently refactor, but don't ignore it either.\n- Preserve compatibility; break it only for an explicit, stated reason.\n- For protocol behavior, identify the relevant RFC and verify conformance against the existing implementation.\n\n## Source control\n\n- Never run history- or remote-mutating commands (`git commit`, `git push`, `git tag`, `gh pr create`) unless explicitly asked. Stage diffs; the human reviews, commits, and pushes.\n- Propose a commit message in Conventional Commit format, imperative mood.\n- Update `CHANGELOG.md` under `### Unreleased`: one terse clause per change, least markup. Rationale belongs in the code or PR, not the bullet.\n\n## Coding standards\n\n- Target current Node LTS; prefer ES2024 over legacy patterns.\n- Existing code is CommonJS (`require`/`exports`) — match it. New modules should use ESM with CJS interop (see `@haraka/email-address`).\n- Add `node:` prefixes to built-in requires in any file you touch (`require('fs')` → `require('node:fs')`).\n- Prefer: promise APIs (`fs/promises`), `for...of`/`for...in` over `forEach`, `node:readline` for line parsing, template literals over concatenation, `true`/`false` over `1`/`0`, and guard-style early returns.\n- Remove commented-out code (it lives in git history). `npm run qlty` must pass without warnings.\n\n## Comments\n\n- Prefer self-documenting code: a better name beats a comment.\n- Keep only WHY comments — a hidden constraint, an invariant, a workaround for a specific bug, or an RFC citation that explains otherwise-surprising behavior.\n- Delete WHAT comments that restate the code, and comments that narrate history or audit findings. If a rename makes a comment redundant, delete it rather than updating it.\n\n## Haraka plugins\n\n- Full hook/API reference: `docs/Plugins.md` in the `Haraka` core repo.\n- A plugin is an npm package: `index.js` (`exports.register` + hook handlers), `config/` (default `.ini`/`.json`/`.yaml`), `test/`, `README.md`.\n- Register hooks in `exports.register` with `this.register_hook('phase', 'method'[, priority])`.\n- Hook handlers take `(next, connection)` (rcpt hooks also take `rcpt`) and must call `next` exactly once. Gate early — return `next()` on missing transaction, disabled config, or skip conditions. Signal a verdict with `next(DENY|DENYSOFT|OK, msg)`; `DENY`/`OK`/etc. are plugin-scope globals (no import).\n- Results: `connection.transaction.results.add(this, { pass|fail|skip|msg|err, emit })`; query with `results.has(plugin, list, search)`. `emit: true` already logs the collated line — don't also `loginfo`/`logerror` the same thing. results.add(this, {err}) always logs.\n- Config loads via `config.get` with a hot-reload callback; declare every boolean or it stays a string and `=== true/false` silently fails:\n  ```js\n  this.cfg = this.config.get('name.ini', { booleans: ['+a.b', '-c.d'] }, () => this.load())\n  ```\n- Keep handlers thin. Push pure decision logic and I/O into `lib/*.js` as pure functions that return a verdict/value; the handler just maps that to `results.add` + `next`. For external I/O (DNS, network), expose an injectable seam — a swappable function whose default is the real implementation — so tests run without mocks.\n- If you add files outside `index.js` (e.g. a `lib/` dir), add them to `package.json` `files` so they publish.\n\n## Testing\n\n- Test real behavior and observable outcomes — `results`, return codes, emitted headers, side effects — not how a function was called. Asserting call shape (`calledWith`, arity, call counts) tests the test and hides signature drift.\n- Mocks/stubs are a smell. Prefer real inputs; when you must isolate a dependency, inject a seam and assert the outcome. Never leave a stub that neuters the path under test — that yields green tests proving nothing.\n- For bug fixes, add a failing test first, then fix.\n- Every feature ships with meaningful tests. A `.skip` is a coverage hole: fix it or delete it.\n- Use `node:test` and `node:assert/strict` for new tests and Mocha migrations. Plugin tests use `haraka-test-fixtures` (`makePlugin`, `makeConnection`, `callHook`).\n- Run the package's `lint`, `prettier`, and `format` before handoff.\n\n## Commands (run inside the target package)\n\n- Test: `npm test`. Single file: `node --test test/path/to/file.js`.\n- Haraka core repo only: `./run_tests [test/plugins/foo.js]`.\n- Coverage: `npm run test:coverage`; lcov: `npm run test:coverage:lcov`. Keep coverage at/above ~90%.\n- If coverage output includes non-source files (e.g. `package.json`, `test/*`), scope it with `--test-coverage-include` (preferred when the list is short) or `--test-coverage-exclude`.\n- Lint/format: `npm run lint` / `prettier` / `format`. Version drift: `npm run versions[:fix]`.\n\n## Package script parity\n\n- node:test packages should expose `test`, `test:coverage`, `test:coverage:lcov`, `lint`, `prettier`, `format` with matching shapes across siblings. Standardize on node:test coverage (not c8); add the canonical scripts when touching a package that lacks them:\n  ```jsonc\n  \"test:coverage\": \"node --test --experimental-test-coverage\",\n  \"test:coverage:lcov\": \"mkdir -p coverage && node --test --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=coverage/lcov.info\"\n  ```\n\n## Repo badges\n\n- Code climate is deprecated. Update with qlty.sh instead.\n- The canonical format for badges should be:\n  - Top of README.md:\n    - [![Test][ci-img]][ci-url] [![Cover][cov-img]][cov-url] [![Qlty][qlty-img]][qlty-url]\n  - Bottom of README.md:\n    - [ci-img]: https://github.com/haraka/<name>/actions/workflows/ci.yml/badge.svg\n    - [ci-url]: https://github.com/haraka/<name>/actions/workflows/ci.yml\n    - [cov-img]: https://codecov.io/github/haraka/<name>/coverage.svg\n    - [cov-url]: https://codecov.io/github/haraka/<name>\n    - [qlty-img]: https://qlty.sh/gh/haraka/projects/<name>/maintainability.svg\n    - [qlty-url]: https://qlty.sh/gh/haraka/projects/<name>\n",".github/copilot-instructions.md":"<!-- agents-version: 2 -->\n\n# AGENTS.md\n\nShared instructions for every coding agent in this repo. The tool-specific files\n(`CLAUDE.md`, `GEMINI.md`, `.github/copilot-instructions.md`) defer to this one.\nIf a package-level instruction file exists, it is authoritative for that package.\n\n## Repository model\n\n- This file is shared verbatim across every Haraka repository, so it describes the whole family — not just the repo you are in. Each repo is an independent npm package with its own `package.json`, tests, and version history; there is no root workspace or shared test runner. Run every command from the package root.\n- Repos are worked on both standalone and as sibling checkouts in a combined tree (`Haraka/`, `plugin/<name>/`, …). Don't assume sibling packages are present on disk.\n- The family:\n  - `Haraka` — core SMTP server (has `./run_tests`).\n  - `haraka-plugin-<name>` — optional plugins; may depend on each other (e.g. bounce → spf).\n  - `haraka-config` — config loader with hot-reload.\n  - `haraka-results`, `haraka-notes` — per-connection result / note tracking.\n  - `haraka-net-utils`, `haraka-utils`, `haraka-constants`, `haraka-dsn`, `haraka-tld`, `haraka-message-stream` — shared libraries.\n  - `@haraka/email-address` — the RFC 5321/5322 parser (supersedes the deprecated `address-rfc282x`).\n  - `@haraka/eslint-config`, `haraka-test-fixtures`.\n\n## Working agreement\n\n- Do only what was asked. When you spot an adjacent bug or smell, surface it and ask before expanding scope — don't silently refactor, but don't ignore it either.\n- Preserve compatibility; break it only for an explicit, stated reason.\n- For protocol behavior, identify the relevant RFC and verify conformance against the existing implementation.\n\n## Source control\n\n- Never run history- or remote-mutating commands (`git commit`, `git push`, `git tag`, `gh pr create`) unless explicitly asked. Stage diffs; the human reviews, commits, and pushes.\n- Propose a commit message in Conventional Commit format, imperative mood.\n- Update `CHANGELOG.md` under `### Unreleased`: one terse clause per change, least markup. Rationale belongs in the code or PR, not the bullet.\n\n## Coding standards\n\n- Target current Node LTS; prefer ES2024 over legacy patterns.\n- Existing code is CommonJS (`require`/`exports`) — match it. New modules should use ESM with CJS interop (see `@haraka/email-address`).\n- Add `node:` prefixes to built-in requires in any file you touch (`require('fs')` → `require('node:fs')`).\n- Prefer: promise APIs (`fs/promises`), `for...of`/`for...in` over `forEach`, `node:readline` for line parsing, template literals over concatenation, `true`/`false` over `1`/`0`, and guard-style early returns.\n- Remove commented-out code (it lives in git history). `npm run qlty` must pass without warnings.\n\n## Comments\n\n- Prefer self-documenting code: a better name beats a comment.\n- Keep only WHY comments — a hidden constraint, an invariant, a workaround for a specific bug, or an RFC citation that explains otherwise-surprising behavior.\n- Delete WHAT comments that restate the code, and comments that narrate history or audit findings. If a rename makes a comment redundant, delete it rather than updating it.\n\n## Haraka plugins\n\n- Full hook/API reference: `docs/Plugins.md` in the `Haraka` core repo.\n- A plugin is an npm package: `index.js` (`exports.register` + hook handlers), `config/` (default `.ini`/`.json`/`.yaml`), `test/`, `README.md`.\n- Register hooks in `exports.register` with `this.register_hook('phase', 'method'[, priority])`.\n- Hook handlers take `(next, connection)` (rcpt hooks also take `rcpt`) and must call `next` exactly once. Gate early — return `next()` on missing transaction, disabled config, or skip conditions. Signal a verdict with `next(DENY|DENYSOFT|OK, msg)`; `DENY`/`OK`/etc. are plugin-scope globals (no import).\n- Results: `connection.transaction.results.add(this, { pass|fail|skip|msg|err, emit })`; query with `results.has(plugin, list, search)`. `emit: true` already logs the collated line — don't also `loginfo`/`logerror` the same thing. results.add(this, {err}) always logs.\n- Config loads via `config.get` with a hot-reload callback; declare every boolean or it stays a string and `=== true/false` silently fails:\n  ```js\n  this.cfg = this.config.get('name.ini', { booleans: ['+a.b', '-c.d'] }, () => this.load())\n  ```\n- Keep handlers thin. Push pure decision logic and I/O into `lib/*.js` as pure functions that return a verdict/value; the handler just maps that to `results.add` + `next`. For external I/O (DNS, network), expose an injectable seam — a swappable function whose default is the real implementation — so tests run without mocks.\n- If you add files outside `index.js` (e.g. a `lib/` dir), add them to `package.json` `files` so they publish.\n\n## Testing\n\n- Test real behavior and observable outcomes — `results`, return codes, emitted headers, side effects — not how a function was called. Asserting call shape (`calledWith`, arity, call counts) tests the test and hides signature drift.\n- Mocks/stubs are a smell. Prefer real inputs; when you must isolate a dependency, inject a seam and assert the outcome. Never leave a stub that neuters the path under test — that yields green tests proving nothing.\n- For bug fixes, add a failing test first, then fix.\n- Every feature ships with meaningful tests. A `.skip` is a coverage hole: fix it or delete it.\n- Use `node:test` and `node:assert/strict` for new tests and Mocha migrations. Plugin tests use `haraka-test-fixtures` (`makePlugin`, `makeConnection`, `callHook`).\n- Run the package's `lint`, `prettier`, and `format` before handoff.\n\n## Commands (run inside the target package)\n\n- Test: `npm test`. Single file: `node --test test/path/to/file.js`.\n- Haraka core repo only: `./run_tests [test/plugins/foo.js]`.\n- Coverage: `npm run test:coverage`; lcov: `npm run test:coverage:lcov`. Keep coverage at/above ~90%.\n- If coverage output includes non-source files (e.g. `package.json`, `test/*`), scope it with `--test-coverage-include` (preferred when the list is short) or `--test-coverage-exclude`.\n- Lint/format: `npm run lint` / `prettier` / `format`. Version drift: `npm run versions[:fix]`.\n\n## Package script parity\n\n- node:test packages should expose `test`, `test:coverage`, `test:coverage:lcov`, `lint`, `prettier`, `format` with matching shapes across siblings. Standardize on node:test coverage (not c8); add the canonical scripts when touching a package that lacks them:\n  ```jsonc\n  \"test:coverage\": \"node --test --experimental-test-coverage\",\n  \"test:coverage:lcov\": \"mkdir -p coverage && node --test --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=coverage/lcov.info\"\n  ```\n\n## Repo badges\n\n- Code climate is deprecated. Update with qlty.sh instead.\n- The canonical format for badges should be:\n  - Top of README.md:\n    - [![Test][ci-img]][ci-url] [![Cover][cov-img]][cov-url] [![Qlty][qlty-img]][qlty-url]\n  - Bottom of README.md:\n    - [ci-img]: https://github.com/haraka/<name>/actions/workflows/ci.yml/badge.svg\n    - [ci-url]: https://github.com/haraka/<name>/actions/workflows/ci.yml\n    - [cov-img]: https://codecov.io/github/haraka/<name>/coverage.svg\n    - [cov-url]: https://codecov.io/github/haraka/<name>\n    - [qlty-img]: https://qlty.sh/gh/haraka/projects/<name>/maintainability.svg\n    - [qlty-url]: https://qlty.sh/gh/haraka/projects/<name>\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","title":"AGENTS.md","content":"<!-- agents-version: 2 -->\n\n# AGENTS.md\n\nShared instructions for every coding agent in this repo. The tool-specific files\n(`CLAUDE.md`, `GEMINI.md`, `.github/copilot-instructions.md`) defer to this one.\nIf a package-level instruction file exists, it is authoritative for that package.\n\n## Repository model\n\n- This file is shared verbatim across every Haraka repository, so it describes the whole family — not just the repo you are in. Each repo is an independent npm package with its own `package.json`, tests, and version history; there is no root workspace or shared test runner. Run every command from the package root.\n- Repos are worked on both standalone and as sibling checkouts in a combined tree (`Haraka/`, `plugin/<name>/`, …). Don't assume sibling packages are present on disk.\n- The family:\n  - `Haraka` — core SMTP server (has `./run_tests`).\n  - `haraka-plugin-<name>` — optional plugins; may depend on each other (e.g. bounce → spf).\n  - `haraka-config` — config loader with hot-reload.\n  - `haraka-results`, `haraka-notes` — per-connection result / note tracking.\n  - `haraka-net-utils`, `haraka-utils`, `haraka-constants`, `haraka-dsn`, `haraka-tld`, `haraka-message-stream` — shared libraries.\n  - `@haraka/email-address` — the RFC 5321/5322 parser (supersedes the deprecated `address-rfc282x`).\n  - `@haraka/eslint-config`, `haraka-test-fixtures`.\n\n## Working agreement\n\n- Do only what was asked. When you spot an adjacent bug or smell, surface it and ask before expanding scope — don't silently refactor, but don't ignore it either.\n- Preserve compatibility; break it only for an explicit, stated reason.\n- For protocol behavior, identify the relevant RFC and verify conformance against the existing implementation.\n\n## Source control\n\n- Never run history- or remote-mutating commands (`git commit`, `git push`, `git tag`, `gh pr create`) unless explicitly asked. Stage diffs; the human reviews, commits, and pushes.\n- Propose a commit message in Conventional Commit format, imperative mood.\n- Update `CHANGELOG.md` under `### Unreleased`: one terse clause per change, least markup. Rationale belongs in the code or PR, not the bullet.\n\n## Coding standards\n\n- Target current Node LTS; prefer ES2024 over legacy patterns.\n- Existing code is CommonJS (`require`/`exports`) — match it. New modules should use ESM with CJS interop (see `@haraka/email-address`).\n- Add `node:` prefixes to built-in requires in any file you touch (`require('fs')` → `require('node:fs')`).\n- Prefer: promise APIs (`fs/promises`), `for...of`/`for...in` over `forEach`, `node:readline` for line parsing, template literals over concatenation, `true`/`false` over `1`/`0`, and guard-style early returns.\n- Remove commented-out code (it lives in git history). `npm run qlty` must pass without warnings.\n\n## Comments\n\n- Prefer self-documenting code: a better name beats a comment.\n- Keep only WHY comments — a hidden constraint, an invariant, a workaround for a specific bug, or an RFC citation that explains otherwise-surprising behavior.\n- Delete WHAT comments that restate the code, and comments that narrate history or audit findings. If a rename makes a comment redundant, delete it rather than updating it.\n\n## Haraka plugins\n\n- Full hook/API reference: `docs/Plugins.md` in the `Haraka` core repo.\n- A plugin is an npm package: `index.js` (`exports.register` + hook handlers), `config/` (default `.ini`/`.json`/`.yaml`), `test/`, `README.md`.\n- Register hooks in `exports.register` with `this.register_hook('phase', 'method'[, priority])`.\n- Hook handlers take `(next, connection)` (rcpt hooks also take `rcpt`) and must call `next` exactly once. Gate early — return `next()` on missing transaction, disabled config, or skip conditions. Signal a verdict with `next(DENY|DENYSOFT|OK, msg)`; `DENY`/`OK`/etc. are plugin-scope globals (no import).\n- Results: `connection.transaction.results.add(this, { pass|fail|skip|msg|err, emit })`; query with `results.has(plugin, list, search)`. `emit: true` already logs the collated line — don't also `loginfo`/`logerror` the same thing. results.add(this, {err}) always logs.\n- Config loads via `config.get` with a hot-reload callback; declare every boolean or it stays a string and `=== true/false` silently fails:\n  ```js\n  this.cfg = this.config.get('name.ini', { booleans: ['+a.b', '-c.d'] }, () => this.load())\n  ```\n- Keep handlers thin. Push pure decision logic and I/O into `lib/*.js` as pure functions that return a verdict/value; the handler just maps that to `results.add` + `next`. For external I/O (DNS, network), expose an injectable seam — a swappable function whose default is the real implementation — so tests run without mocks.\n- If you add files outside `index.js` (e.g. a `lib/` dir), add them to `package.json` `files` so they publish.\n\n## Testing\n\n- Test real behavior and observable outcomes — `results`, return codes, emitted headers, side effects — not how a function was called. Asserting call shape (`calledWith`, arity, call counts) tests the test and hides signature drift.\n- Mocks/stubs are a smell. Prefer real inputs; when you must isolate a dependency, inject a seam and assert the outcome. Never leave a stub that neuters the path under test — that yields green tests proving nothing.\n- For bug fixes, add a failing test first, then fix.\n- Every feature ships with meaningful tests. A `.skip` is a coverage hole: fix it or delete it.\n- Use `node:test` and `node:assert/strict` for new tests and Mocha migrations. Plugin tests use `haraka-test-fixtures` (`makePlugin`, `makeConnection`, `callHook`).\n- Run the package's `lint`, `prettier`, and `format` before handoff.\n\n## Commands (run inside the target package)\n\n- Test: `npm test`. Single file: `node --test test/path/to/file.js`.\n- Haraka core repo only: `./run_tests [test/plugins/foo.js]`.\n- Coverage: `npm run test:coverage`; lcov: `npm run test:coverage:lcov`. Keep coverage at/above ~90%.\n- If coverage output includes non-source files (e.g. `package.json`, `test/*`), scope it with `--test-coverage-include` (preferred when the list is short) or `--test-coverage-exclude`.\n- Lint/format: `npm run lint` / `prettier` / `format`. Version drift: `npm run versions[:fix]`.\n\n## Package script parity\n\n- node:test packages should expose `test`, `test:coverage`, `test:coverage:lcov`, `lint`, `prettier`, `format` with matching shapes across siblings. Standardize on node:test coverage (not c8); add the canonical scripts when touching a package that lacks them:\n  ```jsonc\n  \"test:coverage\": \"node --test --experimental-test-coverage\",\n  \"test:coverage:lcov\": \"mkdir -p coverage && node --test --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=coverage/lcov.info\"\n  ```\n\n## Repo badges\n\n- Code climate is deprecated. Update with qlty.sh instead.\n- The canonical format for badges should be:\n  - Top of README.md:\n    - [![Test][ci-img]][ci-url] [![Cover][cov-img]][cov-url] [![Qlty][qlty-img]][qlty-url]\n  - Bottom of README.md:\n    - [ci-img]: https://github.com/haraka/<name>/actions/workflows/ci.yml/badge.svg\n    - [ci-url]: https://github.com/haraka/<name>/actions/workflows/ci.yml\n    - [cov-img]: https://codecov.io/github/haraka/<name>/coverage.svg\n    - [cov-url]: https://codecov.io/github/haraka/<name>\n    - [qlty-img]: https://qlty.sh/gh/haraka/projects/<name>/maintainability.svg\n    - [qlty-url]: https://qlty.sh/gh/haraka/projects/<name>\n","category":"root","tokens":1852},{"name":"copilot-instructions.md","path":".github/copilot-instructions.md","title":"copilot-instructions.md","content":"<!-- agents-version: 2 -->\n\n# AGENTS.md\n\nShared instructions for every coding agent in this repo. The tool-specific files\n(`CLAUDE.md`, `GEMINI.md`, `.github/copilot-instructions.md`) defer to this one.\nIf a package-level instruction file exists, it is authoritative for that package.\n\n## Repository model\n\n- This file is shared verbatim across every Haraka repository, so it describes the whole family — not just the repo you are in. Each repo is an independent npm package with its own `package.json`, tests, and version history; there is no root workspace or shared test runner. Run every command from the package root.\n- Repos are worked on both standalone and as sibling checkouts in a combined tree (`Haraka/`, `plugin/<name>/`, …). Don't assume sibling packages are present on disk.\n- The family:\n  - `Haraka` — core SMTP server (has `./run_tests`).\n  - `haraka-plugin-<name>` — optional plugins; may depend on each other (e.g. bounce → spf).\n  - `haraka-config` — config loader with hot-reload.\n  - `haraka-results`, `haraka-notes` — per-connection result / note tracking.\n  - `haraka-net-utils`, `haraka-utils`, `haraka-constants`, `haraka-dsn`, `haraka-tld`, `haraka-message-stream` — shared libraries.\n  - `@haraka/email-address` — the RFC 5321/5322 parser (supersedes the deprecated `address-rfc282x`).\n  - `@haraka/eslint-config`, `haraka-test-fixtures`.\n\n## Working agreement\n\n- Do only what was asked. When you spot an adjacent bug or smell, surface it and ask before expanding scope — don't silently refactor, but don't ignore it either.\n- Preserve compatibility; break it only for an explicit, stated reason.\n- For protocol behavior, identify the relevant RFC and verify conformance against the existing implementation.\n\n## Source control\n\n- Never run history- or remote-mutating commands (`git commit`, `git push`, `git tag`, `gh pr create`) unless explicitly asked. Stage diffs; the human reviews, commits, and pushes.\n- Propose a commit message in Conventional Commit format, imperative mood.\n- Update `CHANGELOG.md` under `### Unreleased`: one terse clause per change, least markup. Rationale belongs in the code or PR, not the bullet.\n\n## Coding standards\n\n- Target current Node LTS; prefer ES2024 over legacy patterns.\n- Existing code is CommonJS (`require`/`exports`) — match it. New modules should use ESM with CJS interop (see `@haraka/email-address`).\n- Add `node:` prefixes to built-in requires in any file you touch (`require('fs')` → `require('node:fs')`).\n- Prefer: promise APIs (`fs/promises`), `for...of`/`for...in` over `forEach`, `node:readline` for line parsing, template literals over concatenation, `true`/`false` over `1`/`0`, and guard-style early returns.\n- Remove commented-out code (it lives in git history). `npm run qlty` must pass without warnings.\n\n## Comments\n\n- Prefer self-documenting code: a better name beats a comment.\n- Keep only WHY comments — a hidden constraint, an invariant, a workaround for a specific bug, or an RFC citation that explains otherwise-surprising behavior.\n- Delete WHAT comments that restate the code, and comments that narrate history or audit findings. If a rename makes a comment redundant, delete it rather than updating it.\n\n## Haraka plugins\n\n- Full hook/API reference: `docs/Plugins.md` in the `Haraka` core repo.\n- A plugin is an npm package: `index.js` (`exports.register` + hook handlers), `config/` (default `.ini`/`.json`/`.yaml`), `test/`, `README.md`.\n- Register hooks in `exports.register` with `this.register_hook('phase', 'method'[, priority])`.\n- Hook handlers take `(next, connection)` (rcpt hooks also take `rcpt`) and must call `next` exactly once. Gate early — return `next()` on missing transaction, disabled config, or skip conditions. Signal a verdict with `next(DENY|DENYSOFT|OK, msg)`; `DENY`/`OK`/etc. are plugin-scope globals (no import).\n- Results: `connection.transaction.results.add(this, { pass|fail|skip|msg|err, emit })`; query with `results.has(plugin, list, search)`. `emit: true` already logs the collated line — don't also `loginfo`/`logerror` the same thing. results.add(this, {err}) always logs.\n- Config loads via `config.get` with a hot-reload callback; declare every boolean or it stays a string and `=== true/false` silently fails:\n  ```js\n  this.cfg = this.config.get('name.ini', { booleans: ['+a.b', '-c.d'] }, () => this.load())\n  ```\n- Keep handlers thin. Push pure decision logic and I/O into `lib/*.js` as pure functions that return a verdict/value; the handler just maps that to `results.add` + `next`. For external I/O (DNS, network), expose an injectable seam — a swappable function whose default is the real implementation — so tests run without mocks.\n- If you add files outside `index.js` (e.g. a `lib/` dir), add them to `package.json` `files` so they publish.\n\n## Testing\n\n- Test real behavior and observable outcomes — `results`, return codes, emitted headers, side effects — not how a function was called. Asserting call shape (`calledWith`, arity, call counts) tests the test and hides signature drift.\n- Mocks/stubs are a smell. Prefer real inputs; when you must isolate a dependency, inject a seam and assert the outcome. Never leave a stub that neuters the path under test — that yields green tests proving nothing.\n- For bug fixes, add a failing test first, then fix.\n- Every feature ships with meaningful tests. A `.skip` is a coverage hole: fix it or delete it.\n- Use `node:test` and `node:assert/strict` for new tests and Mocha migrations. Plugin tests use `haraka-test-fixtures` (`makePlugin`, `makeConnection`, `callHook`).\n- Run the package's `lint`, `prettier`, and `format` before handoff.\n\n## Commands (run inside the target package)\n\n- Test: `npm test`. Single file: `node --test test/path/to/file.js`.\n- Haraka core repo only: `./run_tests [test/plugins/foo.js]`.\n- Coverage: `npm run test:coverage`; lcov: `npm run test:coverage:lcov`. Keep coverage at/above ~90%.\n- If coverage output includes non-source files (e.g. `package.json`, `test/*`), scope it with `--test-coverage-include` (preferred when the list is short) or `--test-coverage-exclude`.\n- Lint/format: `npm run lint` / `prettier` / `format`. Version drift: `npm run versions[:fix]`.\n\n## Package script parity\n\n- node:test packages should expose `test`, `test:coverage`, `test:coverage:lcov`, `lint`, `prettier`, `format` with matching shapes across siblings. Standardize on node:test coverage (not c8); add the canonical scripts when touching a package that lacks them:\n  ```jsonc\n  \"test:coverage\": \"node --test --experimental-test-coverage\",\n  \"test:coverage:lcov\": \"mkdir -p coverage && node --test --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=coverage/lcov.info\"\n  ```\n\n## Repo badges\n\n- Code climate is deprecated. Update with qlty.sh instead.\n- The canonical format for badges should be:\n  - Top of README.md:\n    - [![Test][ci-img]][ci-url] [![Cover][cov-img]][cov-url] [![Qlty][qlty-img]][qlty-url]\n  - Bottom of README.md:\n    - [ci-img]: https://github.com/haraka/<name>/actions/workflows/ci.yml/badge.svg\n    - [ci-url]: https://github.com/haraka/<name>/actions/workflows/ci.yml\n    - [cov-img]: https://codecov.io/github/haraka/<name>/coverage.svg\n    - [cov-url]: https://codecov.io/github/haraka/<name>\n    - [qlty-img]: https://qlty.sh/gh/haraka/projects/<name>/maintainability.svg\n    - [qlty-url]: https://qlty.sh/gh/haraka/projects/<name>\n","category":".github","tokens":1852}]}