diff --git a/.wolf/.gitignore b/.wolf/.gitignore new file mode 100644 index 0000000..ebcbce5 --- /dev/null +++ b/.wolf/.gitignore @@ -0,0 +1,26 @@ +# OpenWolf: machine-local state (committed state: cerebrum.md, STATUS.md, +# memory.md, buglog.json, anatomy.md, anatomy-index.json, config.json, OPENWOLF.md) +hooks/ +backups/ +cache/ +daemon.log +daemon.pid +dashboard-token +token-ledger.json +usage-report.json +updates/ +handoff/ +activity/ +usage/ +archive/ +session-memory/ +ledger-pending/ +ledger-sessions/ +bug-pending/ +*.pending/ +suggestions.json +cron-state.json +_scan-state.json +_*.json +*.lock +*.tmp diff --git a/.wolf/OPENWOLF.md b/.wolf/OPENWOLF.md new file mode 100644 index 0000000..9f81c47 --- /dev/null +++ b/.wolf/OPENWOLF.md @@ -0,0 +1,44 @@ +# OpenWolf Operating Protocol + +You are working in an OpenWolf-managed project. These rules apply every turn. + +OpenWolf's hooks handle the bookkeeping: they maintain `.wolf/anatomy.md` and `.wolf/memory.md` after writes, track reads, and surface anatomy hints when you read files. Do not update those two files manually unless your agent has no OpenWolf hooks installed (Gemini CLI, Cursor). + +## STATUS.md: read first, keep fresh + +`.wolf/STATUS.md` is the handoff document. Read it FIRST when resuming a session; it replaces re-reading memory, plans, and code to reconstruct context. + +Keep it fresh: when the user signals a quest is done ("done", "ship it", "next phase", "/clear", "wrap up"), move finished items to the done section, write the next quest (objective, files, decisions), and bump the date. Do this before responding "done" on any multi-file task and before suggesting `/clear`. A stale STATUS.md wastes the next session. + +## File navigation + +1. Before reading an unfamiliar file, grep `.wolf/anatomy.md` for its path to get a one-line description and token estimate. Do NOT read anatomy.md whole; it is an index, not a document. +2. If the description answers your question, skip the full read. For large files, prefer Read with offset/limit over whole-file reads. +3. If a file is not in anatomy.md, search with Grep/Glob. Regenerate the index with `openwolf scan`. + +## Code generation + +1. Before generating code, check `.wolf/cerebrum.md`: respect `## Do-Not-Repeat` (past mistakes), `## Key Learnings`, and `## User Preferences`. +2. Update cerebrum.md whenever you learn something: a user correction or preference, a project convention not obvious from code, an API surprise, a gotcha that would trip a fresh session, a significant decision and its why. The bar is LOW; a redundant entry costs nothing, a missing one repeats the discovery next session. + +## Bug logging + +Before fixing any bug: grep `.wolf/buglog.json` for the error message or filename; the fix may already be known. + +After fixing any bug, failed test, failed build, or user-reported problem: append an entry with `id`, `timestamp`, `error_message`, `file`, `root_cause`, `fix`, `tags`, `occurrences`, `last_seen`. Also log when you edit a file more than twice to get it right. The threshold is LOW. + +## Token discipline + +- Never re-read a file already read this session unless it changed since. +- Prefer anatomy descriptions and targeted Grep over full file reads. +- If appending to a file, do not read the entire file first. + +## Session end + +Before wrapping up: update `.wolf/STATUS.md`, write a one-line session summary to `.wolf/memory.md` (`| HH:MM | description | file(s) | outcome | ~tokens |`), and record any learnings or bugs from the session in cerebrum.md / buglog.json. + +## On-demand skills + +- `/designqc`: screenshot-based design review of the running app (uses `openwolf designqc`). +- `/reframe`: UI framework selection, migration, and anti-generic design audits. +- `/security-audit`: security review of the project. diff --git a/.wolf/STATUS.md b/.wolf/STATUS.md new file mode 100644 index 0000000..bfda8cf --- /dev/null +++ b/.wolf/STATUS.md @@ -0,0 +1,68 @@ +--- +description: session handoff, regenerate with /handoff when a quest finishes +budget_tokens: 1000 +--- +# STATUS β€” LambdaMail + +> Single source of truth for resuming work. Read this FIRST when starting a session. +> Update this file at the end of every work phase so the next `/clear` resumes in 1 read. +> Last updated: 2026-10-06 + +--- + +## βœ… Done + + + +- (nothing yet β€” fill in as work completes) + +--- + +## πŸš€ Next phase + +**Goal:** __ + +### Acceptance criteria +1. __ +2. _<...>_ + +### Files to create / edit +| Type | File | Content | +|---|---|---| +| new | `path/to/file.ts` | _what it does_ | + +### Closed decisions +- __ + +### Open decisions +- __ + +--- + +## πŸ“ Active architecture + +- **Stack:** __ +- **Key tables / modules:** __ +- **Patterns:** __ + +--- + +## ⚠️ External blockers (don't block coding) + +- __ + +--- + +## πŸ”§ Useful commands + +```bash +# add the most-used commands here so the next session has them ready +``` + +--- + +## πŸ“š References (read IF needed) + +- `.wolf/cerebrum.md` β€” User Preferences + Do-Not-Repeat + Decision Log +- `.wolf/anatomy.md` β€” token-efficient file index +- `.wolf/buglog.json` β€” known bugs + fixes diff --git a/.wolf/anatomy-index.json b/.wolf/anatomy-index.json new file mode 100644 index 0000000..3e83f4e --- /dev/null +++ b/.wolf/anatomy-index.json @@ -0,0 +1,19634 @@ +{ + "version": 1, + "meta": { + "lastScanned": "2026-10-22T17:41:17.634Z", + "fileCount": 439, + "hits": 0, + "misses": 0, + "renderedHash": "6ed8dc6b806503f41452e51121e5f78379f1c88655d4934d082d2aa065428b8c", + "storeUpdatedAt": "2026-10-22T17:41:17.650Z", + "rootHash": "098b934a6205dce4" + }, + "files": { + ".github/workflows/ci.yml": { + "description": "CI: CI", + "tokens": 2438, + "hash": "6c726813e25592ee", + "size": 8535, + "mtimeMs": 1787397529469.2144, + "updatedAt": "2026-10-06T11:06:20.841Z", + "source": "scan", + "importance": 0.0144 + }, + ".github/workflows/release.yml": { + "description": "CI: Release", + "tokens": 1863, + "hash": "2807c0a181b06985", + "size": 6519, + "mtimeMs": 1785692139305.374, + "updatedAt": "2026-10-06T11:06:20.842Z", + "source": "scan", + "importance": 0.0144 + }, + ".gitignore": { + "description": "Git ignore rules", + "tokens": 277, + "hash": "3301027e4578d04e", + "size": 1037, + "mtimeMs": 1788778069441.6716, + "updatedAt": "2026-10-06T11:06:20.842Z", + "source": "scan", + "importance": 0.0144 + }, + ".trivyignore": { + "description": "Findings that have been assessed and deliberately accepted.", + "tokens": 211, + "hash": "086e96cfd209780d", + "size": 790, + "mtimeMs": 1787397483064.9219, + "updatedAt": "2026-10-06T11:06:20.842Z", + "source": "scan", + "importance": 0.0144 + }, + "CLAUDE.md": { + "description": "OpenWolf", + "tokens": 99, + "hash": "074d6b42b583729a", + "size": 395, + "mtimeMs": 1791284775282.7249, + "updatedAt": "2026-10-06T11:06:20.842Z", + "source": "scan", + "importance": 0.0144 + }, + "config/rspamd/actions.conf": { + "description": "Score thresholds from PLAN.md section 10.1. The principle behind them is", + "tokens": 108, + "hash": "997da1d5eb2d54f9", + "size": 405, + "mtimeMs": 1785635482520.1724, + "updatedAt": "2026-10-06T11:06:20.843Z", + "source": "scan", + "importance": 0.0181 + }, + "config/rspamd/logging.inc": { + "description": "", + "tokens": 10, + "hash": "5e34af32b6cbb124", + "size": 34, + "mtimeMs": 1785635482520.2253, + "updatedAt": "2026-10-06T11:06:20.844Z", + "source": "scan", + "importance": 0.0144 + }, + "config/rspamd/options.inc": { + "description": "DNS resolution is needed for RBL, SPF and DKIM lookups.", + "tokens": 33, + "hash": "280d53ae02c1e9d1", + "size": 121, + "mtimeMs": 1785635482520.2253, + "updatedAt": "2026-10-06T11:06:20.844Z", + "source": "scan", + "importance": 0.0144 + }, + "config/rspamd/ratelimit.conf": { + "description": "Rate limiting of PLAN.md section 5.2.", + "tokens": 408, + "hash": "c37c19abd0aa6b90", + "size": 1530, + "mtimeMs": 1785668486700.7197, + "updatedAt": "2026-10-06T11:06:20.844Z", + "source": "scan", + "importance": 0.0144 + }, + "config/rspamd/worker-normal.inc": { + "description": "The protocols service reaches Rspamd over the internal network only; the", + "tokens": 40, + "hash": "abfc9851ef69b44d", + "size": 148, + "mtimeMs": 1785635482520.2253, + "updatedAt": "2026-10-06T11:06:20.844Z", + "source": "scan", + "importance": 0.0144 + }, + "docker-compose.build.yaml": { + "description": "Development overlay: builds the images from source instead of pulling them.", + "tokens": 260, + "hash": "e862c03c258b9a59", + "size": 908, + "mtimeMs": 1785692122263.221, + "updatedAt": "2026-10-06T11:06:20.844Z", + "source": "scan", + "importance": 0.0144 + }, + "docker-compose.yaml": { + "description": "Docker Compose services", + "tokens": 5222, + "hash": "d6d3729e516bd862", + "size": 18275, + "mtimeMs": 1788807731559.9775, + "updatedAt": "2026-10-06T11:06:20.844Z", + "source": "scan", + "importance": 0.0144 + }, + "docs/DEPLOYMENT.md": { + "description": "Deploying LambdaMail on Coolify", + "tokens": 2605, + "hash": "23b07b4d184e1730", + "size": 10464, + "mtimeMs": 1785711428683.7927, + "updatedAt": "2026-10-06T11:06:20.845Z", + "source": "scan", + "importance": 0.0144 + }, + "docs/OPERATIONS.md": { + "description": "Operating LambdaMail", + "tokens": 2229, + "hash": "617a161087828ea6", + "size": 8944, + "mtimeMs": 1785778509187.322, + "updatedAt": "2026-10-06T11:06:20.845Z", + "source": "scan", + "importance": 0.0144 + }, + "LICENSE": { + "description": "Project license", + "tokens": 287, + "hash": "e780f4e3f551768e", + "size": 1075, + "mtimeMs": 1785635482520.0276, + "updatedAt": "2026-10-06T11:06:20.845Z", + "source": "scan", + "importance": 0.0144 + }, + "Makefile": { + "description": "Make build targets", + "tokens": 1327, + "hash": "7506c906b1ffbc30", + "size": 4973, + "mtimeMs": 1787399200409.531, + "updatedAt": "2026-10-06T11:06:20.845Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0001_init_schema.down.sql": { + "description": "0001_init_schema.down.sql", + "tokens": 225, + "hash": "6bfb7c7ab5b65af0", + "size": 787, + "mtimeMs": 1785635482520.2253, + "updatedAt": "2026-10-06T11:06:20.845Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0001_init_schema.up.sql": { + "description": "0001_init_schema.up.sql", + "tokens": 5730, + "hash": "b8bcc5da5448ba80", + "size": 20052, + "mtimeMs": 1785635482520.4055, + "updatedAt": "2026-10-06T11:06:20.845Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0002_add_is_system_to_aliases.down.sql": { + "description": "Revert is_system column from aliases table", + "tokens": 29, + "hash": "ed0d47f60dcb0456", + "size": 99, + "mtimeMs": 1785635482520.4055, + "updatedAt": "2026-10-06T11:06:20.845Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0002_add_is_system_to_aliases.up.sql": { + "description": "Add is_system column to aliases table to track mandatory system security aliases", + "tokens": 45, + "hash": "b6258683379a5fdc", + "size": 157, + "mtimeMs": 1785635482520.4055, + "updatedAt": "2026-10-06T11:06:20.845Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0003_create_report_tables.down.sql": { + "description": "", + "tokens": 47, + "hash": "80de1e969b66afb7", + "size": 163, + "mtimeMs": 1785635482520.4055, + "updatedAt": "2026-10-06T11:06:20.846Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0003_create_report_tables.up.sql": { + "description": "0003_create_report_tables.up.sql", + "tokens": 575, + "hash": "549fda0d4b844504", + "size": 2011, + "mtimeMs": 1785635482520.4055, + "updatedAt": "2026-10-06T11:06:20.846Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0004_index_outbound_stale_locks.down.sql": { + "description": "", + "tokens": 14, + "hash": "43223c9fbdf02741", + "size": 47, + "mtimeMs": 1785635482520.4055, + "updatedAt": "2026-10-06T11:06:20.846Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0004_index_outbound_stale_locks.up.sql": { + "description": "Delivery workers claim a job by moving it to SENDING with a lock deadline.", + "tokens": 105, + "hash": "baa10f42842f0201", + "size": 367, + "mtimeMs": 1785635482520.4055, + "updatedAt": "2026-10-06T11:06:20.847Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0005_create_acme_tables.down.sql": { + "description": "", + "tokens": 34, + "hash": "6151953d87fde01a", + "size": 116, + "mtimeMs": 1785635482520.4055, + "updatedAt": "2026-10-06T11:06:20.847Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0005_create_acme_tables.up.sql": { + "description": "Self-managed ACME state (PLAN.md section 8.3, mode B).", + "tokens": 752, + "hash": "0aa695b02e33aa1a", + "size": 2630, + "mtimeMs": 1785635482520.4055, + "updatedAt": "2026-10-06T11:06:20.847Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0006_add_user_settings_and_system_config.down.sql": { + "description": "SQL: 2 alter(s)", + "tokens": 44, + "hash": "103c3bf8fea142c6", + "size": 153, + "mtimeMs": 1785689161515.933, + "updatedAt": "2026-10-06T11:06:20.847Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0006_add_user_settings_and_system_config.up.sql": { + "description": "SQL: tables: system_config, 2 alter(s)", + "tokens": 135, + "hash": "8395986d4cdf6857", + "size": 471, + "mtimeMs": 1785689161515.983, + "updatedAt": "2026-10-06T11:06:20.847Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0007_reconcile_folder_counters.down.sql": { + "description": "Irreversible by nature: this migration replaced incorrect counter values and", + "tokens": 90, + "hash": "2332763d0547e4a2", + "size": 315, + "mtimeMs": 1787249604286.1067, + "updatedAt": "2026-10-06T11:06:20.847Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0007_reconcile_folder_counters.up.sql": { + "description": "Reconcile folders.unread_count and folders.total_count with the messages", + "tokens": 819, + "hash": "da54d4d9ddb6784a", + "size": 2864, + "mtimeMs": 1787249604285.1064, + "updatedAt": "2026-10-06T11:06:20.847Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0008_add_reports_folder.down.sql": { + "description": "Only removes the folder where it is still empty. A Reports folder holding", + "tokens": 94, + "hash": "a23977c7b38c02f7", + "size": 329, + "mtimeMs": 1787315228591.184, + "updatedAt": "2026-10-06T11:06:20.848Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0008_add_reports_folder.up.sql": { + "description": "Give every existing mailbox a Reports folder.", + "tokens": 185, + "hash": "c01f6a6618bcc58f", + "size": 646, + "mtimeMs": 1787315228588.1838, + "updatedAt": "2026-10-06T11:06:20.848Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0009_report_ingestion_idempotency.down.sql": { + "description": "", + "tokens": 30, + "hash": "621d79fa620b8da9", + "size": 102, + "mtimeMs": 1787315393216.618, + "updatedAt": "2026-10-06T11:06:20.848Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0009_report_ingestion_idempotency.up.sql": { + "description": "Make storing a report idempotent, keyed on the reporter and their own id", + "tokens": 425, + "hash": "320ac4e78f25edf2", + "size": 1487, + "mtimeMs": 1787315417511.682, + "updatedAt": "2026-10-06T11:06:20.848Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0010_vacation_reply_log.down.sql": { + "description": "", + "tokens": 12, + "hash": "81ffcfe2d090d24a", + "size": 39, + "mtimeMs": 1787422419521.8015, + "updatedAt": "2026-10-06T11:06:20.848Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0010_vacation_reply_log.up.sql": { + "description": "Who has already been told the mailbox is unattended.", + "tokens": 257, + "hash": "8954550462b77a7e", + "size": 899, + "mtimeMs": 1787422419519.8015, + "updatedAt": "2026-10-06T11:06:20.848Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0011_backfill_markers.down.sql": { + "description": "", + "tokens": 10, + "hash": "fc074b30dfa9a39b", + "size": 32, + "mtimeMs": 1787585011256.0364, + "updatedAt": "2026-10-06T11:06:20.848Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0011_backfill_markers.up.sql": { + "description": "Records one-shot data repairs so they run once rather than on every start.", + "tokens": 176, + "hash": "a0ab5c231b2122e6", + "size": 615, + "mtimeMs": 1787585011255.0364, + "updatedAt": "2026-10-06T11:06:20.848Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0012_mailbox_avatars.down.sql": { + "description": "", + "tokens": 11, + "hash": "64aa33bf273d00dc", + "size": 38, + "mtimeMs": 1788781505182.072, + "updatedAt": "2026-10-06T11:06:20.848Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0012_mailbox_avatars.up.sql": { + "description": "The profile photo a mailbox shows inside LambdaMail.", + "tokens": 256, + "hash": "0460bbe887839880", + "size": 893, + "mtimeMs": 1788781505182.5593, + "updatedAt": "2026-10-06T11:06:20.848Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0013_domain_bimi.down.sql": { + "description": "", + "tokens": 10, + "hash": "1e8f64ffca56231e", + "size": 34, + "mtimeMs": 1788781505182.647, + "updatedAt": "2026-10-06T11:06:20.848Z", + "source": "scan", + "importance": 0.0144 + }, + "migrations/0013_domain_bimi.up.sql": { + "description": "The BIMI logo a domain publishes, and the certificate that vouches for it.", + "tokens": 188, + "hash": "93f8969e37f23682", + "size": 655, + "mtimeMs": 1788781505182.7427, + "updatedAt": "2026-10-06T11:06:20.848Z", + "source": "scan", + "importance": 0.0144 + }, + "README.md": { + "description": "Project documentation", + "tokens": 3456, + "hash": "c6fefaffc5dd6cae", + "size": 14009, + "mtimeMs": 1788830725023.0046, + "updatedAt": "2026-10-06T11:06:20.848Z", + "source": "scan", + "importance": 0.0144 + }, + "scripts/dev-seed.sql": { + "description": "Dev-only fixture data for local SMTP testing. Not a schema migration -", + "tokens": 533, + "hash": "828b747673281f01", + "size": 1863, + "mtimeMs": 1785689161516.0613, + "updatedAt": "2026-10-06T11:06:20.848Z", + "source": "scan", + "importance": 0.0144 + }, + "scripts/lang-lint.mjs": { + "description": "Fails the build if tracked source contains non-ASCII characters or common", + "tokens": 483, + "hash": "175b90835152afa1", + "size": 1811, + "mtimeMs": 1785635482520.7053, + "updatedAt": "2026-10-06T11:06:20.848Z", + "source": "scan", + "importance": 0.0144 + }, + "services/auth/Dockerfile": { + "description": "Docker container definition", + "tokens": 408, + "hash": "23ae46fa25d62dae", + "size": 1528, + "mtimeMs": 1785690291764.2104, + "updatedAt": "2026-10-06T11:06:20.849Z", + "source": "scan", + "importance": 0.0144 + }, + "services/auth/package.json": { + "description": "Node.js package manifest", + "tokens": 138, + "hash": "b0f8b2bbfe6d650f", + "size": 482, + "mtimeMs": 1785689161516.3496, + "updatedAt": "2026-10-06T11:06:20.849Z", + "source": "scan", + "importance": 0.0144 + }, + "services/auth/src/auth.test.ts": { + "description": "Declares password", + "tokens": 3292, + "hash": "ea5e64b5eee24c1f", + "size": 11522, + "mtimeMs": 1787075233035.2646, + "updatedAt": "2026-10-06T11:06:20.850Z", + "source": "scan", + "imports": [ + "services/auth/src/crypto.ts", + "services/auth/src/totp.ts", + "services/auth/src/mfaHelpers.ts", + "services/auth/src/session.ts" + ], + "importance": 0.0144 + }, + "services/auth/src/cli.ts": { + "description": "Operator commands for a fresh deployment.", + "tokens": 2135, + "hash": "ed7ae6509490d1f4", + "size": 7472, + "mtimeMs": 1785763147980.08, + "updatedAt": "2026-10-06T11:06:20.850Z", + "source": "scan", + "symbols": [ + { + "name": "fail", + "kind": "fn", + "startLine": 29, + "endLine": 32, + "tokens": 28 + }, + { + "name": "parseAddress", + "kind": "fn", + "startLine": 34, + "endLine": 38, + "tokens": 87 + }, + { + "name": "createAdmin", + "kind": "fn", + "startLine": 40, + "endLine": 95, + "tokens": 715 + }, + { + "name": "resetPassword", + "kind": "fn", + "startLine": 97, + "endLine": 112, + "tokens": 236 + }, + { + "name": "resetMfa", + "kind": "fn", + "startLine": 128, + "endLine": 148, + "tokens": 266 + }, + { + "name": "main", + "kind": "fn", + "startLine": 150, + "endLine": 175, + "tokens": 287 + } + ], + "symbolSource": "ts", + "skeleton": "function fail(message: string): never {\nfunction parseAddress(email: string): { localPart: string; domain: string } {\nasync function createAdmin(email: string, password: string, force: boolean): Promise {\nasync function resetPassword(email: string, password: string): Promise {\nasync function resetMfa(email: string): Promise {\nasync function main(): Promise {", + "imports": [ + "services/auth/src/db.ts", + "services/auth/src/crypto.ts" + ], + "importance": 0.0144 + }, + "services/auth/src/crypto.ts": { + "description": "Derives the key-encryption key. Unlike the previous implementation there is", + "tokens": 1465, + "hash": "d889db98e91aeefb", + "size": 5125, + "mtimeMs": 1785777410816.2126, + "updatedAt": "2026-10-06T11:06:20.850Z", + "source": "scan", + "symbols": [ + { + "name": "getMasterKey", + "kind": "fn", + "startLine": 21, + "endLine": 29, + "tokens": 94 + }, + { + "name": "waiting", + "kind": "fn", + "startLine": 47, + "endLine": 47, + "tokens": 11 + }, + { + "name": "withHashSlot", + "kind": "fn", + "startLine": 49, + "endLine": 62, + "tokens": 124 + }, + { + "name": "hashPassword", + "kind": "fn", + "startLine": 65, + "endLine": 75, + "tokens": 101 + }, + { + "name": "verifyPassword", + "kind": "fn", + "startLine": 86, + "endLine": 100, + "tokens": 183 + }, + { + "name": "encryptSecret", + "kind": "fn", + "startLine": 102, + "endLine": 108, + "tokens": 115 + }, + { + "name": "decryptSecret", + "kind": "fn", + "startLine": 110, + "endLine": 119, + "tokens": 170 + }, + { + "name": "safeEqual", + "kind": "fn", + "startLine": 122, + "endLine": 127, + "tokens": 62 + } + ], + "symbolSource": "ts", + "importance": 0.0488 + }, + "services/auth/src/db.ts": { + "description": "Lazily opens the shared connection pool. It is lazy so that unit tests which", + "tokens": 266, + "hash": "386c4c9804e452c6", + "size": 929, + "mtimeMs": 1785689161516.6565, + "updatedAt": "2026-10-06T11:06:20.850Z", + "source": "scan", + "importance": 0.0285 + }, + "services/auth/src/diagnostics.test.ts": { + "description": "Declares UserFacts", + "tokens": 1317, + "hash": "0d090657b251613b", + "size": 4607, + "mtimeMs": 1787574283038.489, + "updatedAt": "2026-10-06T11:06:20.850Z", + "source": "scan", + "symbols": [ + { + "name": "user", + "kind": "fn", + "startLine": 6, + "endLine": 20, + "tokens": 99 + }, + { + "name": "keys", + "kind": "fn", + "startLine": 22, + "endLine": 22, + "tokens": 20 + }, + { + "name": "statusOf", + "kind": "fn", + "startLine": 23, + "endLine": 24, + "tokens": 35 + }, + { + "name": "server", + "kind": "fn", + "startLine": 74, + "endLine": 81, + "tokens": 72 + } + ], + "symbolSource": "ts", + "imports": [ + "services/auth/src/diagnostics.ts" + ], + "importance": 0.0144 + }, + "services/auth/src/diagnostics.ts": { + "description": "Checks an operator can run by hand, against the whole server or one user.", + "tokens": 1557, + "hash": "bfbe0920c9ff7428", + "size": 5449, + "mtimeMs": 1787574257845.43, + "updatedAt": "2026-10-06T11:06:20.851Z", + "source": "scan", + "symbols": [ + { + "name": "CheckStatus", + "kind": "section", + "startLine": 9, + "endLine": 9, + "tokens": 18 + }, + { + "name": "Check", + "kind": "section", + "startLine": 11, + "endLine": 18, + "tokens": 71 + }, + { + "name": "UserFacts", + "kind": "section", + "startLine": 20, + "endLine": 33, + "tokens": 99 + }, + { + "name": "checkUser", + "kind": "fn", + "startLine": 38, + "endLine": 104, + "tokens": 638 + }, + { + "name": "ServerFacts", + "kind": "section", + "startLine": 106, + "endLine": 112, + "tokens": 65 + }, + { + "name": "checkServer", + "kind": "fn", + "startLine": 114, + "endLine": 160, + "tokens": 447 + }, + { + "name": "overallStatus", + "kind": "fn", + "startLine": 163, + "endLine": 168, + "tokens": 76 + } + ], + "symbolSource": "ts", + "importance": 0.0295 + }, + "services/auth/src/health.test.ts": { + "description": "Declares withServer", + "tokens": 470, + "hash": "02929edf5bd0abf7", + "size": 1645, + "mtimeMs": 1785635482520.933, + "updatedAt": "2026-10-06T11:06:20.851Z", + "source": "scan", + "imports": [ + "services/auth/src/health.ts" + ], + "importance": 0.0144 + }, + "services/auth/src/health.ts": { + "description": "Builds the /health/live and /health/ready handler shared by the auth and", + "tokens": 215, + "hash": "4e0751ac405a206c", + "size": 751, + "mtimeMs": 1785635482521.343, + "updatedAt": "2026-10-06T11:06:20.851Z", + "source": "scan", + "importance": 0.0328 + }, + "services/auth/src/imageValidation.test.ts": { + "description": "Declares png", + "tokens": 741, + "hash": "5fd87ad138545c03", + "size": 2593, + "mtimeMs": 1788830725023.8179, + "updatedAt": "2026-10-06T11:06:20.852Z", + "source": "scan", + "symbols": [ + { + "name": "png", + "kind": "fn", + "startLine": 4, + "endLine": 4, + "tokens": 27 + }, + { + "name": "jpeg", + "kind": "fn", + "startLine": 5, + "endLine": 5, + "tokens": 18 + }, + { + "name": "webp", + "kind": "fn", + "startLine": 6, + "endLine": 6, + "tokens": 31 + } + ], + "symbolSource": "ts", + "imports": [ + "services/auth/src/imageValidation.ts" + ], + "importance": 0.0144 + }, + "services/auth/src/imageValidation.ts": { + "description": "What an uploaded avatar is allowed to be.", + "tokens": 598, + "hash": "c8d2c6aa16a00d0e", + "size": 2091, + "mtimeMs": 1788830725024.844, + "updatedAt": "2026-10-06T11:06:20.852Z", + "source": "scan", + "symbols": [ + { + "name": "AvatarType", + "kind": "section", + "startLine": 11, + "endLine": 11, + "tokens": 20 + }, + { + "name": "Sniffed", + "kind": "section", + "startLine": 13, + "endLine": 15, + "tokens": 16 + }, + { + "name": "startsWith", + "kind": "fn", + "startLine": 17, + "endLine": 18, + "tokens": 44 + }, + { + "name": "sniffImage", + "kind": "fn", + "startLine": 27, + "endLine": 41, + "tokens": 141 + }, + { + "name": "AvatarRejection", + "kind": "section", + "startLine": 43, + "endLine": 43, + "tokens": 21 + }, + { + "name": "AvatarCheck", + "kind": "section", + "startLine": 45, + "endLine": 49, + "tokens": 30 + }, + { + "name": "checkAvatar", + "kind": "fn", + "startLine": 52, + "endLine": 62, + "tokens": 134 + } + ], + "symbolSource": "ts", + "importance": 0.0295 + }, + "services/auth/src/mfaHelpers.ts": { + "description": "Generates single-use recovery codes. Only the hashes are ever stored, so a", + "tokens": 494, + "hash": "4845667e8009ac10", + "size": 1727, + "mtimeMs": 1785768397826.4392, + "updatedAt": "2026-10-06T11:06:20.852Z", + "source": "scan", + "imports": [ + "services/auth/src/crypto.ts" + ], + "importance": 0.0203 + }, + "services/auth/src/pagination.test.ts": { + "description": "Declares at", + "tokens": 661, + "hash": "b78f86539fb17341", + "size": 2313, + "mtimeMs": 1787573680119.6714, + "updatedAt": "2026-10-06T11:06:20.852Z", + "source": "scan", + "symbols": [ + { + "name": "at", + "kind": "fn", + "startLine": 4, + "endLine": 4, + "tokens": 16 + } + ], + "symbolSource": "ts", + "imports": [ + "services/auth/src/pagination.ts" + ], + "importance": 0.0144 + }, + "services/auth/src/pagination.ts": { + "description": "Page and filter parameters, read from a URL and made safe for SQL.", + "tokens": 510, + "hash": "d3ca3931bf3bf444", + "size": 1784, + "mtimeMs": 1787573560529.7898, + "updatedAt": "2026-10-06T11:06:20.852Z", + "source": "scan", + "symbols": [ + { + "name": "PageRequest", + "kind": "section", + "startLine": 15, + "endLine": 20, + "tokens": 30 + }, + { + "name": "positiveInt", + "kind": "fn", + "startLine": 26, + "endLine": 33, + "tokens": 102 + }, + { + "name": "parsePageParams", + "kind": "fn", + "startLine": 35, + "endLine": 40, + "tokens": 109 + }, + { + "name": "paged", + "kind": "fn", + "startLine": 43, + "endLine": 51, + "tokens": 62 + } + ], + "symbolSource": "ts", + "importance": 0.0295 + }, + "services/auth/src/repository.ts": { + "description": "Decides whether this account must present a second factor now.", + "tokens": 14732, + "hash": "f28dc25af4a5e019", + "size": 51559, + "mtimeMs": 1788830725025.3042, + "updatedAt": "2026-10-06T11:06:20.853Z", + "source": "scan", + "symbols": [ + { + "name": "MailboxRecord", + "kind": "section", + "startLine": 12, + "endLine": 23, + "tokens": 83 + }, + { + "name": "LoginOutcome", + "kind": "section", + "startLine": 25, + "endLine": 29, + "tokens": 57 + }, + { + "name": "findMailboxByEmail", + "kind": "fn", + "startLine": 31, + "endLine": 40, + "tokens": 116 + }, + { + "name": "hasConfirmedTotp", + "kind": "fn", + "startLine": 42, + "endLine": 48, + "tokens": 82 + }, + { + "name": "isMfaRequired", + "kind": "fn", + "startLine": 59, + "endLine": 61, + "tokens": 35 + }, + { + "name": "isMfaEnrollmentRequired", + "kind": "fn", + "startLine": 64, + "endLine": 68, + "tokens": 68 + }, + { + "name": "authenticate", + "kind": "fn", + "startLine": 77, + "endLine": 108, + "tokens": 323 + }, + { + "name": "registerFailedLogin", + "kind": "fn", + "startLine": 110, + "endLine": 121, + "tokens": 138 + }, + { + "name": "startTotpEnrollment", + "kind": "fn", + "startLine": 146, + "endLine": 174, + "tokens": 318 + }, + { + "name": "resetTotpEnrollment", + "kind": "fn", + "startLine": 183, + "endLine": 185, + "tokens": 51 + }, + { + "name": "TotpRow", + "kind": "section", + "startLine": 187, + "endLine": 193, + "tokens": 40 + }, + { + "name": "loadTotp", + "kind": "fn", + "startLine": 195, + "endLine": 202, + "tokens": 96 + }, + { + "name": "verifyPendingTotp", + "kind": "fn", + "startLine": 214, + "endLine": 225, + "tokens": 138 + }, + { + "name": "markTotpConfirmed", + "kind": "fn", + "startLine": 228, + "endLine": 233, + "tokens": 65 + }, + { + "name": "verifyTotpForLogin", + "kind": "fn", + "startLine": 240, + "endLine": 250, + "tokens": 148 + }, + { + "name": "storeRecoveryCodes", + "kind": "fn", + "startLine": 254, + "endLine": 259, + "tokens": 93 + }, + { + "name": "consumeRecoveryCode", + "kind": "fn", + "startLine": 266, + "endLine": 288, + "tokens": 289 + }, + { + "name": "countUnusedRecoveryCodes", + "kind": "fn", + "startLine": 290, + "endLine": 296, + "tokens": 85 + }, + { + "name": "listAppPasswords", + "kind": "fn", + "startLine": 300, + "endLine": 305, + "tokens": 88 + }, + { + "name": "createAppPassword", + "kind": "fn", + "startLine": 307, + "endLine": 314, + "tokens": 104 + }, + { + "name": "deleteAppPassword", + "kind": "fn", + "startLine": 316, + "endLine": 322, + "tokens": 78 + }, + { + "name": "recordSession", + "kind": "fn", + "startLine": 326, + "endLine": 339, + "tokens": 139 + }, + { + "name": "setLocale", + "kind": "fn", + "startLine": 341, + "endLine": 343, + "tokens": 55 + }, + { + "name": "dashboardStats", + "kind": "fn", + "startLine": 351, + "endLine": 379, + "tokens": 456 + }, + { + "name": "AdminScope", + "kind": "section", + "startLine": 381, + "endLine": 384, + "tokens": 20 + }, + { + "name": "scopeClause", + "kind": "fn", + "startLine": 389, + "endLine": 392, + "tokens": 66 + }, + { + "name": "listDomains", + "kind": "fn", + "startLine": 394, + "endLine": 403, + "tokens": 128 + }, + { + "name": "UserListFilter", + "kind": "section", + "startLine": 405, + "endLine": 411, + "tokens": 40 + }, + { + "name": "listMailboxes", + "kind": "fn", + "startLine": 420, + "endLine": 467, + "tokens": 495 + }, + { + "name": "dmarcSummary", + "kind": "fn", + "startLine": 469, + "endLine": 489, + "tokens": 294 + } + ], + "symbolSource": "ts", + "skeleton": "export interface MailboxRecord {\nexport type LoginOutcome =\nexport async function findMailboxByEmail(email: string): Promise {\nexport async function hasConfirmedTotp(mailboxId: string): Promise {\nexport async function isMfaRequired(mailbox: MailboxRecord): Promise {\nexport function isMfaEnrollmentRequired(mailbox: MailboxRecord): boolean {\nexport async function authenticate(email: string, password: string): Promise {\nasync function registerFailedLogin(mailboxId: string): Promise {\nexport async function startTotpEnrollment(\nexport async function resetTotpEnrollment(mailboxId: string): Promise {\ninterface TotpRow {\nasync function loadTotp(mailboxId: string, status: \"PENDING\" | \"CONFIRMED\"): Promise {\nexport async function verifyPendingTotp(\nexport async function markTotpConfirmed(id: string, step: number): Promise {\nexport async function verifyTotpForLogin(mailboxId: string, code: string): Promise {\nexport async function storeRecoveryCodes(mailboxId: string, hashes: string[]): Promise {\nexport async function consumeRecoveryCode(mailboxId: string, code: string, ip: string | null): Promise {\nexport async function countUnusedRecoveryCodes(mailboxId: string): Promise {", + "imports": [ + "services/auth/src/db.ts", + "services/auth/src/crypto.ts", + "services/auth/src/totp.ts" + ], + "importance": 0.0173 + }, + "services/auth/src/router.test.ts": { + "description": "Enrolls a real second factor and returns the shared secret.", + "tokens": 16096, + "hash": "d51286f73008c107", + "size": 56336, + "mtimeMs": 1788830725025.798, + "updatedAt": "2026-10-06T11:06:20.853Z", + "source": "scan", + "symbols": [ + { + "name": "startServer", + "kind": "fn", + "startLine": 27, + "endLine": 40, + "tokens": 115 + }, + { + "name": "seed", + "kind": "fn", + "startLine": 42, + "endLine": 62, + "tokens": 256 + }, + { + "name": "userEmail", + "kind": "fn", + "startLine": 64, + "endLine": 64, + "tokens": 18 + }, + { + "name": "adminEmail", + "kind": "fn", + "startLine": 65, + "endLine": 65, + "tokens": 18 + }, + { + "name": "post", + "kind": "fn", + "startLine": 67, + "endLine": 77, + "tokens": 114 + }, + { + "name": "put", + "kind": "fn", + "startLine": 79, + "endLine": 89, + "tokens": 114 + }, + { + "name": "get", + "kind": "fn", + "startLine": 91, + "endLine": 96, + "tokens": 77 + }, + { + "name": "enrollTotp", + "kind": "fn", + "startLine": 99, + "endLine": 109, + "tokens": 183 + }, + { + "name": "adminSession", + "kind": "fn", + "startLine": 119, + "endLine": 131, + "tokens": 188 + } + ], + "symbolSource": "ts", + "skeleton": "function startServer(): Promise {\nasync function seed(): Promise {\nconst userEmail = () => `user@authtest-${DOMAIN_ID}.invalid`;\nconst adminEmail = () => `admin@authtest-${DOMAIN_ID}.invalid`;\nasync function post(path: string, body: unknown, token?: string) {\nasync function put(path: string, body: unknown, token?: string) {\nasync function get(path: string, token?: string) {\nasync function enrollTotp(mailboxId: string, token: string): Promise {\nasync function adminSession(): Promise {", + "imports": [ + "services/auth/src/router.ts", + "services/auth/src/crypto.ts", + "services/auth/src/db.ts", + "services/auth/src/totp.ts" + ], + "importance": 0.0144 + }, + "services/auth/src/router.ts": { + "description": "Cookie scoped to the whole origin: the API lives under /api, so a cookie", + "tokens": 15221, + "hash": "c081eb9e92416477", + "size": 53271, + "mtimeMs": 1788830725026.2295, + "updatedAt": "2026-10-06T11:06:20.854Z", + "source": "scan", + "symbols": [ + { + "name": "sessionCookie", + "kind": "fn", + "startLine": 28, + "endLine": 32, + "tokens": 74 + }, + { + "name": "readBody", + "kind": "fn", + "startLine": 34, + "endLine": 52, + "tokens": 166 + }, + { + "name": "clientIp", + "kind": "fn", + "startLine": 54, + "endLine": 56, + "tokens": 29 + }, + { + "name": "isSecureRequest", + "kind": "fn", + "startLine": 60, + "endLine": 62, + "tokens": 37 + }, + { + "name": "handleApiRequest", + "kind": "fn", + "startLine": 64, + "endLine": 71, + "tokens": 68 + }, + { + "name": "sendJson", + "kind": "fn", + "startLine": 73, + "endLine": 77, + "tokens": 58 + }, + { + "name": "extractToken", + "kind": "fn", + "startLine": 79, + "endLine": 96, + "tokens": 187 + }, + { + "name": "route", + "kind": "fn", + "startLine": 98, + "endLine": 237, + "tokens": 2738 + }, + { + "name": "readBinaryBody", + "kind": "fn", + "startLine": 245, + "endLine": 261, + "tokens": 142 + }, + { + "name": "parseJsonBody", + "kind": "fn", + "startLine": 263, + "endLine": 270, + "tokens": 67 + }, + { + "name": "userSaveAvatar", + "kind": "fn", + "startLine": 281, + "endLine": 309, + "tokens": 270 + }, + { + "name": "getAvatar", + "kind": "fn", + "startLine": 319, + "endLine": 351, + "tokens": 398 + }, + { + "name": "userDeleteAvatar", + "kind": "fn", + "startLine": 353, + "endLine": 359, + "tokens": 89 + }, + { + "name": "login", + "kind": "fn", + "startLine": 363, + "endLine": 435, + "tokens": 960 + }, + { + "name": "adminStepUp", + "kind": "fn", + "startLine": 446, + "endLine": 487, + "tokens": 532 + }, + { + "name": "enrolmentGrant", + "kind": "fn", + "startLine": 490, + "endLine": 504, + "tokens": 104 + }, + { + "name": "issueSession", + "kind": "fn", + "startLine": 519, + "endLine": 571, + "tokens": 500 + }, + { + "name": "mfaVerify", + "kind": "fn", + "startLine": 573, + "endLine": 607, + "tokens": 466 + }, + { + "name": "logout", + "kind": "fn", + "startLine": 609, + "endLine": 615, + "tokens": 78 + }, + { + "name": "me", + "kind": "fn", + "startLine": 629, + "endLine": 640, + "tokens": 148 + }, + { + "name": "updateLocale", + "kind": "fn", + "startLine": 642, + "endLine": 650, + "tokens": 134 + }, + { + "name": "totpEnroll", + "kind": "fn", + "startLine": 652, + "endLine": 674, + "tokens": 280 + }, + { + "name": "totpConfirm", + "kind": "fn", + "startLine": 676, + "endLine": 693, + "tokens": 280 + }, + { + "name": "appPasswordsList", + "kind": "fn", + "startLine": 695, + "endLine": 697, + "tokens": 48 + }, + { + "name": "appPasswordCreate", + "kind": "fn", + "startLine": 699, + "endLine": 708, + "tokens": 171 + }, + { + "name": "appPasswordDelete", + "kind": "fn", + "startLine": 710, + "endLine": 715, + "tokens": 87 + }, + { + "name": "tokenHash", + "kind": "fn", + "startLine": 719, + "endLine": 721, + "tokens": 43 + }, + { + "name": "listSessions", + "kind": "fn", + "startLine": 723, + "endLine": 725, + "tokens": 61 + }, + { + "name": "revokeSession", + "kind": "fn", + "startLine": 727, + "endLine": 732, + "tokens": 83 + }, + { + "name": "revokeOthers", + "kind": "fn", + "startLine": 734, + "endLine": 737, + "tokens": 72 + } + ], + "symbolSource": "ts", + "skeleton": "function sessionCookie(name: string, value: string, maxAge: number, secure: boolean): string {\nfunction readBody(req: IncomingMessage): Promise {\nfunction clientIp(req: IncomingMessage): string | null {\nfunction isSecureRequest(): boolean {\nexport function handleApiRequest(req: IncomingMessage, res: ServerResponse): boolean {\nfunction sendJson(res: ServerResponse, statusCode: number, data: unknown): void {\nfunction extractToken(req: IncomingMessage, url: string): string {\nasync function route(req: IncomingMessage, res: ServerResponse, url: string): Promise {\nfunction readBinaryBody(req: IncomingMessage, maxBytes: number): Promise {\nasync function parseJsonBody(req: IncomingMessage): Promise | null> {\nasync function userSaveAvatar(\nasync function getAvatar(\nasync function userDeleteAvatar(\nasync function login(req: IncomingMessage, res: ServerResponse, surface: \"user\" | \"admin\"): Promise {\nasync function adminStepUp(req: IncomingMessage, res: ServerResponse): Promise {\nfunction enrolmentGrant(mailbox: repo.MailboxRecord): string {\nasync function issueSession(\nasync function mfaVerify(req: IncomingMessage, res: ServerResponse): Promise {\nfunction logout(res: ServerResponse): void {\nasync function me(res: ServerResponse, session: SessionTokenPayload): Promise {", + "imports": [ + "services/auth/src/totp.ts", + "services/auth/src/mfaHelpers.ts", + "services/auth/src/session.ts", + "services/auth/src/repository.ts", + "services/auth/src/pagination.ts", + "services/auth/src/imageValidation.ts", + "services/auth/src/diagnostics.ts" + ], + "importance": 0.0236 + }, + "services/auth/src/server.ts": { + "description": "Declares port", + "tokens": 236, + "hash": "1371507baa838191", + "size": 825, + "mtimeMs": 1785670743191.2344, + "updatedAt": "2026-10-06T11:06:20.854Z", + "source": "scan", + "imports": [ + "services/auth/src/health.ts", + "services/auth/src/router.ts" + ], + "importance": 0.0144 + }, + "services/auth/src/session.ts": { + "description": "The signing secret. There is deliberately no default.", + "tokens": 1406, + "hash": "1afdc1b5e6b134fb", + "size": 4921, + "mtimeMs": 1787075616724.9885, + "updatedAt": "2026-10-06T11:06:20.854Z", + "source": "scan", + "symbols": [ + { + "name": "SessionTokenPayload", + "kind": "section", + "startLine": 3, + "endLine": 29, + "tokens": 365 + }, + { + "name": "JWT_SECRET", + "kind": "fn", + "startLine": 38, + "endLine": 46, + "tokens": 77 + }, + { + "name": "base64UrlEncode", + "kind": "fn", + "startLine": 48, + "endLine": 54, + "tokens": 50 + }, + { + "name": "base64UrlDecode", + "kind": "fn", + "startLine": 56, + "endLine": 60, + "tokens": 60 + }, + { + "name": "createJwt", + "kind": "fn", + "startLine": 62, + "endLine": 90, + "tokens": 266 + }, + { + "name": "verifyJwt", + "kind": "fn", + "startLine": 92, + "endLine": 119, + "tokens": 237 + }, + { + "name": "isSurfaceAuthorized", + "kind": "fn", + "startLine": 121, + "endLine": 137, + "tokens": 187 + }, + { + "name": "isStepUpMfaRequired", + "kind": "fn", + "startLine": 139, + "endLine": 143, + "tokens": 69 + } + ], + "symbolSource": "ts", + "importance": 0.0439 + }, + "services/auth/src/surfaceAccess.test.ts": { + "description": "Which surface a login opens, and what it costs to cross between them.", + "tokens": 1640, + "hash": "14677facd41dded6", + "size": 5740, + "mtimeMs": 1787075616725.0754, + "updatedAt": "2026-10-06T11:06:20.854Z", + "source": "scan", + "imports": [ + "services/auth/src/session.ts", + "services/auth/src/surfaceAccess.ts" + ], + "importance": 0.0144 + }, + "services/auth/src/surfaceAccess.ts": { + "description": "Which surface a login opens, and what crossing between them costs.", + "tokens": 712, + "hash": "def6923a32f17be0", + "size": 2492, + "mtimeMs": 1787075616725.1602, + "updatedAt": "2026-10-06T11:06:20.854Z", + "source": "scan", + "symbols": [ + { + "name": "Surface", + "kind": "section", + "startLine": 17, + "endLine": 17, + "tokens": 12 + }, + { + "name": "secondFactorRequired", + "kind": "fn", + "startLine": 28, + "endLine": 30, + "tokens": 28 + }, + { + "name": "surfacesGrantedBy", + "kind": "fn", + "startLine": 39, + "endLine": 41, + "tokens": 37 + }, + { + "name": "cookieNameFor", + "kind": "fn", + "startLine": 44, + "endLine": 46, + "tokens": 38 + }, + { + "name": "stepUpNeedsSecondFactor", + "kind": "fn", + "startLine": 56, + "endLine": 61, + "tokens": 74 + } + ], + "symbolSource": "ts", + "imports": [ + "services/auth/src/session.ts" + ], + "importance": 0.0205 + }, + "services/auth/src/totp.ts": { + "description": "Renders the otpauth:// URI an authenticator app scans.", + "tokens": 1037, + "hash": "d047f1ad3d771260", + "size": 3629, + "mtimeMs": 1785760808165.6528, + "updatedAt": "2026-10-06T11:06:20.854Z", + "source": "scan", + "symbols": [ + { + "name": "base32Encode", + "kind": "fn", + "startLine": 5, + "endLine": 21, + "tokens": 115 + }, + { + "name": "base32Decode", + "kind": "fn", + "startLine": 23, + "endLine": 39, + "tokens": 140 + }, + { + "name": "getCurrentStep", + "kind": "fn", + "startLine": 41, + "endLine": 43, + "tokens": 41 + }, + { + "name": "generateHotp", + "kind": "fn", + "startLine": 45, + "endLine": 63, + "tokens": 173 + }, + { + "name": "generateTotpUri", + "kind": "fn", + "startLine": 66, + "endLine": 70, + "tokens": 99 + }, + { + "name": "generateTotpSecret", + "kind": "fn", + "startLine": 72, + "endLine": 76, + "tokens": 83 + }, + { + "name": "verifyTotpCode", + "kind": "fn", + "startLine": 78, + "endLine": 110, + "tokens": 341 + } + ], + "symbolSource": "ts", + "importance": 0.0283 + }, + "services/auth/tsconfig.build.json": { + "description": "TypeScript build configuration", + "tokens": 83, + "hash": "a657ee6c4c15f62f", + "size": 289, + "mtimeMs": 1785689161517.313, + "updatedAt": "2026-10-06T11:06:20.854Z", + "source": "scan", + "importance": 0.0144 + }, + "services/auth/tsconfig.json": { + "description": "TypeScript configuration", + "tokens": 88, + "hash": "f5afcc0c2ccf20a7", + "size": 308, + "mtimeMs": 1785635482521.343, + "updatedAt": "2026-10-06T11:06:20.854Z", + "source": "scan", + "importance": 0.0144 + }, + "services/auth/vitest.config.ts": { + "description": "Vitest test configuration", + "tokens": 153, + "hash": "60924390cc1a0cd2", + "size": 534, + "mtimeMs": 1785689161517.3784, + "updatedAt": "2026-10-06T11:06:20.854Z", + "source": "scan", + "importance": 0.0144 + }, + "services/migrations/Dockerfile": { + "description": "Docker container definition", + "tokens": 241, + "hash": "d88fed90d8ad6de3", + "size": 901, + "mtimeMs": 1785692723554.9155, + "updatedAt": "2026-10-06T11:06:20.855Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/.go-arch-lint.yml": { + "description": "health:", + "tokens": 99, + "hash": "6267aade7cb5a743", + "size": 346, + "mtimeMs": 1785635482521.343, + "updatedAt": "2026-10-06T11:06:20.855Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/cmd/lambdamail-protocols/acme_mode.go": { + "description": "tlsaPublisher (26 fields); methods: PublishTlsa", + "tokens": 1204, + "hash": "0293750b8eebf90a", + "size": 4211, + "mtimeMs": 1785635482521.6335, + "updatedAt": "2026-10-06T11:06:20.856Z", + "source": "scan", + "symbols": [ + { + "name": "startAcmeManager", + "kind": "fn", + "startLine": 21, + "endLine": 53, + "tokens": 370 + }, + { + "name": "tlsaPublisher", + "kind": "class", + "startLine": 60, + "endLine": 63, + "tokens": 24 + }, + { + "name": "PublishTlsa", + "kind": "method", + "startLine": 65, + "endLine": 121, + "tokens": 497 + }, + { + "name": "entityTlsaRecord", + "kind": "fn", + "startLine": 123, + "endLine": 131, + "tokens": 58 + }, + { + "name": "equalFoldTrim", + "kind": "fn", + "startLine": 133, + "endLine": 135, + "tokens": 35 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/domain/entity/dkim_key_pair_test.go", + "services/protocols/internal/infrastructure/acme/issuer.go", + "services/protocols/internal/infrastructure/cloudflare/cloudflare_adapter_test.go", + "services/protocols/internal/infrastructure/postgres/acme_repository.go", + "services/protocols/internal/infrastructure/vault/secret_vault_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/cmd/lambdamail-protocols/backfill_attachments.go": { + "description": "Declares attachmentBackfillName", + "tokens": 717, + "hash": "9889e38e6c4805f4", + "size": 2509, + "mtimeMs": 1787585056248.832, + "updatedAt": "2026-10-06T11:06:20.856Z", + "source": "scan", + "symbols": [ + { + "name": "backfillAttachmentFlags", + "kind": "fn", + "startLine": 27, + "endLine": 77, + "tokens": 450 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/usecase/apply_rules_test.go", + "services/protocols/internal/infrastructure/diskstorage/local_disk_blob_reader_test.go", + "services/protocols/internal/infrastructure/postgres/acme_repository.go" + ], + "importance": 0.0144 + }, + "services/protocols/cmd/lambdamail-protocols/backfill_reports.go": { + "description": "", + "tokens": 501, + "hash": "02e82f8ad6077207", + "size": 1752, + "mtimeMs": 1787316189116.0356, + "updatedAt": "2026-10-06T11:06:20.856Z", + "source": "scan", + "symbols": [ + { + "name": "runBackfillReports", + "kind": "fn", + "startLine": 21, + "endLine": 50, + "tokens": 296 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/usecase/apply_rules_test.go", + "services/protocols/internal/infrastructure/diskstorage/local_disk_blob_reader_test.go", + "services/protocols/internal/infrastructure/postgres/acme_repository.go" + ], + "importance": 0.0144 + }, + "services/protocols/cmd/lambdamail-protocols/bimi_store.go": { + "description": "bimiStore (4 fields); methods: Save, Load, Delete", + "tokens": 256, + "hash": "689cf47dbc50d9dd", + "size": 896, + "mtimeMs": 1788830725026.5217, + "updatedAt": "2026-10-06T11:06:20.856Z", + "source": "scan", + "imports": [ + "services/protocols/internal/infrastructure/postgres/acme_repository.go", + "services/protocols/internal/presentation/http/admin_dkim_api.go" + ], + "importance": 0.0144 + }, + "services/protocols/cmd/lambdamail-protocols/config.go": { + "description": "config (52 fields)", + "tokens": 1251, + "hash": "7cf339bfe49c4cfd", + "size": 4376, + "mtimeMs": 1785713002390.445, + "updatedAt": "2026-10-06T11:06:20.856Z", + "source": "scan", + "symbols": [ + { + "name": "config", + "kind": "class", + "startLine": 11, + "endLine": 59, + "tokens": 368 + }, + { + "name": "loadConfig", + "kind": "fn", + "startLine": 61, + "endLine": 109, + "tokens": 614 + }, + { + "name": "domain", + "kind": "method", + "startLine": 113, + "endLine": 121, + "tokens": 56 + }, + { + "name": "envOrDefault", + "kind": "fn", + "startLine": 123, + "endLine": 128, + "tokens": 34 + }, + { + "name": "envInt", + "kind": "fn", + "startLine": 130, + "endLine": 137, + "tokens": 49 + }, + { + "name": "envBool", + "kind": "fn", + "startLine": 139, + "endLine": 146, + "tokens": 52 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/cmd/lambdamail-protocols/disk_other.go": { + "description": "go:build !unix", + "tokens": 98, + "hash": "a86da2188f8776fd", + "size": 343, + "mtimeMs": 1785635482521.721, + "updatedAt": "2026-10-06T11:06:20.856Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/cmd/lambdamail-protocols/disk_unix.go": { + "description": "go:build unix", + "tokens": 183, + "hash": "883831d16fd4f1ef", + "size": 639, + "mtimeMs": 1785635482521.721, + "updatedAt": "2026-10-06T11:06:20.856Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/cmd/lambdamail-protocols/dns_reconciler.go": { + "description": "domainReconciler (14 fields); methods: ReconcileDomain", + "tokens": 961, + "hash": "5418e8c55566b704", + "size": 3362, + "mtimeMs": 1787496300511.3794, + "updatedAt": "2026-10-06T11:06:20.856Z", + "source": "scan", + "symbols": [ + { + "name": "domainReconciler", + "kind": "class", + "startLine": 21, + "endLine": 23, + "tokens": 34 + }, + { + "name": "ReconcileDomain", + "kind": "method", + "startLine": 25, + "endLine": 48, + "tokens": 217 + }, + { + "name": "reconcileDomainOnDemand", + "kind": "fn", + "startLine": 55, + "endLine": 92, + "tokens": 431 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/application/usecase/apply_rules_test.go", + "services/protocols/internal/infrastructure/cloudflare/cloudflare_adapter_test.go", + "services/protocols/internal/infrastructure/netdns/mx_resolver.go", + "services/protocols/internal/infrastructure/postgres/acme_repository.go", + "services/protocols/internal/presentation/http/admin_dkim_api.go" + ], + "importance": 0.0144 + }, + "services/protocols/cmd/lambdamail-protocols/main.go": { + "description": "Command lambdamail-protocols is the entry point for the protocols service.", + "tokens": 8598, + "hash": "221f4737f84e88a6", + "size": 30093, + "mtimeMs": 1788830725027.0022, + "updatedAt": "2026-10-06T11:06:20.857Z", + "source": "scan", + "symbols": [ + { + "name": "main", + "kind": "fn", + "startLine": 51, + "endLine": 71, + "tokens": 110 + }, + { + "name": "run", + "kind": "fn", + "startLine": 73, + "endLine": 290, + "tokens": 2996 + }, + { + "name": "buildCertProvider", + "kind": "fn", + "startLine": 300, + "endLine": 349, + "tokens": 675 + }, + { + "name": "healthy", + "kind": "fn", + "startLine": 353, + "endLine": 353, + "tokens": 10 + }, + { + "name": "watcherDegradedCheck", + "kind": "fn", + "startLine": 358, + "endLine": 375, + "tokens": 250 + }, + { + "name": "relayConfig", + "kind": "fn", + "startLine": 377, + "endLine": 385, + "tokens": 70 + }, + { + "name": "buildScanner", + "kind": "fn", + "startLine": 387, + "endLine": 400, + "tokens": 142 + }, + { + "name": "buildPolicyResolver", + "kind": "fn", + "startLine": 404, + "endLine": 420, + "tokens": 242 + }, + { + "name": "applyMtaStsMode", + "kind": "fn", + "startLine": 422, + "endLine": 431, + "tokens": 127 + }, + { + "name": "startSMTPListeners", + "kind": "fn", + "startLine": 437, + "endLine": 477, + "tokens": 550 + }, + { + "name": "startIMAPListeners", + "kind": "fn", + "startLine": 479, + "endLine": 503, + "tokens": 273 + }, + { + "name": "startPOP3Listeners", + "kind": "fn", + "startLine": 505, + "endLine": 511, + "tokens": 120 + }, + { + "name": "startManageSieveListener", + "kind": "fn", + "startLine": 513, + "endLine": 515, + "tokens": 70 + }, + { + "name": "serveImplicitTLS", + "kind": "fn", + "startLine": 520, + "endLine": 526, + "tokens": 62 + }, + { + "name": "serveInBackground", + "kind": "fn", + "startLine": 531, + "endLine": 538, + "tokens": 70 + }, + { + "name": "mailTLSConfig", + "kind": "fn", + "startLine": 542, + "endLine": 556, + "tokens": 161 + }, + { + "name": "runDeliveryWorker", + "kind": "fn", + "startLine": 558, + "endLine": 572, + "tokens": 106 + }, + { + "name": "runDnsReconciler", + "kind": "fn", + "startLine": 576, + "endLine": 672, + "tokens": 922 + }, + { + "name": "generateDkimKey", + "kind": "fn", + "startLine": 676, + "endLine": 682, + "tokens": 62 + }, + { + "name": "runHealthcheck", + "kind": "fn", + "startLine": 684, + "endLine": 697, + "tokens": 109 + }, + { + "name": "dnsSpecSource", + "kind": "class", + "startLine": 702, + "endLine": 706, + "tokens": 30 + }, + { + "name": "ExpectedRecords", + "kind": "method", + "startLine": 708, + "endLine": 739, + "tokens": 317 + } + ], + "symbolSource": "ts", + "skeleton": "func main() {\nfunc run(cfg config) {\nfunc buildCertProvider(ctx context.Context, cfg config, pool *pgxpool.Pool, secretVault *vault.SecretVault, vaultErr error) (port.CertProvider, func() error) {\nfunc healthy() error { return nil }\nfunc watcherDegradedCheck(watcher *tlsprovider.AcmeCertWatcher, cfg config) func() error {\nfunc relayConfig(cfg config) usecase.RelayConfig {\nfunc buildScanner(cfg config) port.ContentScanner {\nfunc buildPolicyResolver(cfg config) port.TLSPolicyResolver {\nfunc applyMtaStsMode(router *httppresentation.Router, cfg config) {\nfunc startSMTPListeners(cfg config, certProvider port.CertProvider, inboundUC *usecase.ProcessInboundEmailUseCase, submissionUC *usecase.ProcessOutboundEmailUseCase) {\nfunc startIMAPListeners(cfg config, certProvider port.CertProvider, imapUseCase *usecase.ImapSessionUseCase) {\nfunc startPOP3Listeners(certProvider port.CertProvider, pop3UseCase *usecase.Pop3SessionUseCase) {\nfunc startManageSieveListener(certProvider port.CertProvider, sieveUseCase *usecase.ManageSieveSessionUseCase) {\nfunc serveImplicitTLS(addr string, tlsConfig *tls.Config, serve func(net.Listener) error) error {\nfunc serveInBackground(name, addr string, serve func() error) {\nfunc mailTLSConfig(certProvider port.CertProvider) *tls.Config {\nfunc runDeliveryWorker(ctx context.Context, worker *usecase.OutboundWorkerUseCase) {\nfunc runDnsReconciler(", + "imports": [ + "services/protocols/internal/infrastructure/tls/acme_cert_watcher_test.go", + "services/protocols/internal/presentation/http/admin_dkim_api.go", + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/application/usecase/apply_rules_test.go", + "services/protocols/internal/domain/entity/dkim_key_pair_test.go", + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go", + "services/protocols/internal/infrastructure/arc/canonical.go", + "services/protocols/internal/infrastructure/clamav/clamav_adapter_test.go", + "services/protocols/internal/infrastructure/cloudflare/cloudflare_adapter_test.go", + "services/protocols/internal/infrastructure/diskstorage/local_disk_blob_reader_test.go", + "services/protocols/internal/infrastructure/dkim/keygen.go", + "services/protocols/internal/infrastructure/mailauth/authenticator_test.go", + "services/protocols/internal/infrastructure/netdns/mx_resolver.go", + "services/protocols/internal/infrastructure/postgres/acme_repository.go", + "config/rspamd/actions.conf", + "services/protocols/internal/infrastructure/tlspolicy/resolver_test.go", + "services/protocols/internal/infrastructure/vault/secret_vault_test.go", + "services/protocols/internal/presentation/imap/backend_test.go", + "services/protocols/internal/presentation/managesieve/conn_test.go", + "services/protocols/internal/presentation/pop3/conn_test.go", + "services/protocols/internal/presentation/smtp/backend_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/cmd/lambdamail-protocols/preflight.go": { + "description": "", + "tokens": 994, + "hash": "290388f5b73171ea", + "size": 3477, + "mtimeMs": 1785635482521.721, + "updatedAt": "2026-10-06T11:06:20.857Z", + "source": "scan", + "symbols": [ + { + "name": "runPreflight", + "kind": "fn", + "startLine": 18, + "endLine": 39, + "tokens": 191 + }, + { + "name": "buildPreflightDeps", + "kind": "fn", + "startLine": 41, + "endLine": 90, + "tokens": 494 + }, + { + "name": "httpDateTime", + "kind": "fn", + "startLine": 95, + "endLine": 112, + "tokens": 146 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go", + "services/protocols/internal/application/usecase/apply_rules_test.go", + "services/protocols/internal/infrastructure/tls/acme_cert_watcher_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/Dockerfile": { + "description": "Docker container definition", + "tokens": 312, + "hash": "9667f6631a08b9ac", + "size": 1168, + "mtimeMs": 1785668486701.0144, + "updatedAt": "2026-10-06T11:06:20.857Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/go.mod": { + "description": "Go module definition", + "tokens": 326, + "hash": "39b2974fddd7b9a3", + "size": 1220, + "mtimeMs": 1787396966405.9336, + "updatedAt": "2026-10-06T11:06:20.857Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/go.sum": { + "description": "Go dependency checksums", + "tokens": 2706, + "hash": "e76a2a76c9f0b55a", + "size": 10147, + "mtimeMs": 1787396966405.9336, + "updatedAt": "2026-10-06T11:06:20.858Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/application/port/arc_sealer.go": { + "description": "ArcSealer adds an Authenticated Received Chain set to a message", + "tokens": 222, + "hash": "8c054ee7b4e0e303", + "size": 776, + "mtimeMs": 1785635482522.235, + "updatedAt": "2026-10-06T11:06:20.858Z", + "source": "scan", + "importance": 1 + }, + "services/protocols/internal/application/port/auth_repository.go": { + "description": "Interface: AuthRepository (2 methods)", + "tokens": 205, + "hash": "aa25154618e92320", + "size": 716, + "mtimeMs": 1785635482522.3005, + "updatedAt": "2026-10-06T11:06:20.858Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/application/port/blob_reader.go": { + "description": "Interface: BlobReader (2 methods)", + "tokens": 65, + "hash": "5e957cdc87eaffed", + "size": 225, + "mtimeMs": 1785635482522.3005, + "updatedAt": "2026-10-06T11:06:20.858Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/application/port/blob_storage.go": { + "description": "Interface: BlobStorage (2 methods)", + "tokens": 152, + "hash": "c7bfcb8c5bd5b14b", + "size": 532, + "mtimeMs": 1785635482522.3005, + "updatedAt": "2026-10-06T11:06:20.858Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/application/port/cert_provider.go": { + "description": "CertProvider supplies the TLS certificate for STARTTLS. Its signature", + "tokens": 104, + "hash": "487e664e07ccd53c", + "size": 363, + "mtimeMs": 1785635482522.3005, + "updatedAt": "2026-10-06T11:06:20.858Z", + "source": "scan", + "imports": [ + "services/protocols/internal/infrastructure/tls/acme_cert_watcher_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/port/certificate_store.go": { + "description": "Interface: CertificateStore (8 methods)", + "tokens": 524, + "hash": "d8c239239f742d6c", + "size": 1833, + "mtimeMs": 1785635482522.3005, + "updatedAt": "2026-10-06T11:06:20.859Z", + "source": "scan", + "symbols": [ + { + "name": "StoredCertificate", + "kind": "class", + "startLine": 12, + "endLine": 23, + "tokens": 123 + }, + { + "name": "CertificateStore", + "kind": "class", + "startLine": 27, + "endLine": 44, + "tokens": 274 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/application/port/content_scanner.go": { + "description": "Interface: ContentScanner (2 methods)", + "tokens": 93, + "hash": "2924fbefcc6969f5", + "size": 323, + "mtimeMs": 1785635482522.3005, + "updatedAt": "2026-10-06T11:06:20.859Z", + "source": "scan", + "imports": [ + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/port/copy_repository.go": { + "description": "CopiedMessage records one message's UID before and after a COPY/MOVE.", + "tokens": 244, + "hash": "b8a4166d55a8cbc1", + "size": 851, + "mtimeMs": 1785635482522.3005, + "updatedAt": "2026-10-06T11:06:20.859Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/application/port/dkim_repository.go": { + "description": "DkimSigningKey is one active signing key, already decrypted and ready to", + "tokens": 304, + "hash": "09765c37cf3639af", + "size": 1063, + "mtimeMs": 1785635482522.3005, + "updatedAt": "2026-10-06T11:06:20.859Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/application/port/dns_provider.go": { + "description": "Interface: DnsProvider (6 methods)", + "tokens": 168, + "hash": "d153bc9157e80cb1", + "size": 585, + "mtimeMs": 1785635482522.3005, + "updatedAt": "2026-10-06T11:06:20.859Z", + "source": "scan", + "imports": [ + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/port/dns_verifier.go": { + "description": "Interface: DnsVerifier (2 methods)", + "tokens": 190, + "hash": "c0d68724351770d1", + "size": 663, + "mtimeMs": 1785635482522.3005, + "updatedAt": "2026-10-06T11:06:20.859Z", + "source": "scan", + "imports": [ + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/port/expunge_repository.go": { + "description": "ExpungeRepository permanently marks messages as expunged (soft delete -", + "tokens": 208, + "hash": "d1c0509f2c119331", + "size": 728, + "mtimeMs": 1785635482522.3005, + "updatedAt": "2026-10-06T11:06:20.859Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/application/port/flag_repository.go": { + "description": "FlagOp mirrors imap.StoreFlagsOp without importing the imap package here", + "tokens": 222, + "hash": "a50c4e8909720464", + "size": 775, + "mtimeMs": 1785635482522.3005, + "updatedAt": "2026-10-06T11:06:20.860Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/application/port/imap_folder_repository.go": { + "description": "ImapFolderRecord is the IMAP-relevant subset of a folder row.", + "tokens": 185, + "hash": "ace3fb5f03ef6036", + "size": 646, + "mtimeMs": 1785635482522.3005, + "updatedAt": "2026-10-06T11:06:20.860Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/application/port/inbound_message_repository.go": { + "description": "Interface: InboundMessageRepository (2 methods)", + "tokens": 626, + "hash": "6aeebc5c4f7e33e5", + "size": 2191, + "mtimeMs": 1787415911599.8154, + "updatedAt": "2026-10-06T11:06:20.860Z", + "source": "scan", + "symbols": [ + { + "name": "PersistInboundMessageInput", + "kind": "class", + "startLine": 11, + "endLine": 33, + "tokens": 263 + }, + { + "name": "InboundMessageRepository", + "kind": "class", + "startLine": 39, + "endLine": 53, + "tokens": 230 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/application/port/mail_authenticator.go": { + "description": "Interface: MailAuthenticator (2 methods)", + "tokens": 424, + "hash": "29087b2d68b7ee74", + "size": 1481, + "mtimeMs": 1785635482522.3005, + "updatedAt": "2026-10-06T11:06:20.860Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/application/port/mailbox_repository.go": { + "description": "declares the interfaces the application layer depends on.", + "tokens": 386, + "hash": "6f4a3a0079ff0bbb", + "size": 1351, + "mtimeMs": 1785635482522.3005, + "updatedAt": "2026-10-06T11:06:20.860Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/application/port/message_query_repository.go": { + "description": "Interface: MessageQueryRepository (2 methods)", + "tokens": 211, + "hash": "046957cf769ab1fa", + "size": 738, + "mtimeMs": 1785635482522.3005, + "updatedAt": "2026-10-06T11:06:20.860Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/application/port/mx_resolver.go": { + "description": "Interface: MXResolver (2 methods)", + "tokens": 38, + "hash": "c9dba8e8e15caa4e", + "size": 131, + "mtimeMs": 1785635482522.3005, + "updatedAt": "2026-10-06T11:06:20.860Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/application/port/outbound_repository.go": { + "description": "Interface: OutboundRepository (5 methods)", + "tokens": 200, + "hash": "f0deeb88b0890bb1", + "size": 698, + "mtimeMs": 1785635482522.3005, + "updatedAt": "2026-10-06T11:06:20.860Z", + "source": "scan", + "imports": [ + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/port/outbox.go": { + "description": "Interface: OutboxRepository (4 methods)", + "tokens": 237, + "hash": "7cb6272eaad0233e", + "size": 827, + "mtimeMs": 1785689161517.9255, + "updatedAt": "2026-10-06T11:06:20.860Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/application/port/report_repository.go": { + "description": "Interface: ReportRepository (8 methods)", + "tokens": 617, + "hash": "4713b90787d6ea33", + "size": 2158, + "mtimeMs": 1787422396692.923, + "updatedAt": "2026-10-06T11:06:20.860Z", + "source": "scan", + "symbols": [ + { + "name": "ReportRepository", + "kind": "class", + "startLine": 13, + "endLine": 16, + "tokens": 52 + }, + { + "name": "StoredReportMessage", + "kind": "class", + "startLine": 20, + "endLine": 24, + "tokens": 34 + }, + { + "name": "DeliveredReportStore", + "kind": "class", + "startLine": 28, + "endLine": 34, + "tokens": 112 + }, + { + "name": "SieveScriptReader", + "kind": "class", + "startLine": 40, + "endLine": 44, + "tokens": 57 + }, + { + "name": "VacationLog", + "kind": "class", + "startLine": 51, + "endLine": 56, + "tokens": 84 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/port/sieve_repository.go": { + "description": "SieveScriptRecord represents a persistent Sieve script row in PostgreSQL.", + "tokens": 238, + "hash": "d97b9a4e12b7ed1f", + "size": 833, + "mtimeMs": 1785635482522.3005, + "updatedAt": "2026-10-06T11:06:20.860Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/application/port/system_alias_repository.go": { + "description": "SystemAliasRepository manages mandatory system security aliases (postmaster, abuse, dmarc, tlsrpt).", + "tokens": 75, + "hash": "4c4a52b527e321e2", + "size": 262, + "mtimeMs": 1785635482522.3005, + "updatedAt": "2026-10-06T11:06:20.860Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/application/port/tls_policy_resolver.go": { + "description": "Interface: TLSPolicyResolver (2 methods)", + "tokens": 203, + "hash": "e7d648b252c6f7cb", + "size": 710, + "mtimeMs": 1785635482522.3005, + "updatedAt": "2026-10-06T11:06:20.860Z", + "source": "scan", + "imports": [ + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/port/webmail_repository.go": { + "description": "Interface: WebmailRepository (13 methods)", + "tokens": 1306, + "hash": "655086f2c69825bd", + "size": 4570, + "mtimeMs": 1787416293370.6328, + "updatedAt": "2026-10-06T11:06:20.861Z", + "source": "scan", + "symbols": [ + { + "name": "WebmailFolder", + "kind": "class", + "startLine": 12, + "endLine": 18, + "tokens": 63 + }, + { + "name": "WebmailMessage", + "kind": "class", + "startLine": 25, + "endLine": 39, + "tokens": 179 + }, + { + "name": "WebmailMessageBody", + "kind": "class", + "startLine": 42, + "endLine": 46, + "tokens": 36 + }, + { + "name": "WebmailRepository", + "kind": "class", + "startLine": 54, + "endLine": 75, + "tokens": 450 + }, + { + "name": "FolderAdmin", + "kind": "class", + "startLine": 94, + "endLine": 100, + "tokens": 107 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/apply_rules_test.go": { + "description": "stubSieveScripts (35 fields); methods: ActiveScriptFor", + "tokens": 2504, + "hash": "1f7fc40f1c669603", + "size": 8762, + "mtimeMs": 1787417842589.425, + "updatedAt": "2026-10-06T11:06:20.861Z", + "source": "scan", + "symbols": [ + { + "name": "stubSieveScripts", + "kind": "class", + "startLine": 17, + "endLine": 21, + "tokens": 24 + }, + { + "name": "ActiveScriptFor", + "kind": "method", + "startLine": 23, + "endLine": 26, + "tokens": 47 + }, + { + "name": "newRules", + "kind": "fn", + "startLine": 28, + "endLine": 30, + "tokens": 29 + }, + { + "name": "TestRuleDecidesTheFolder", + "kind": "fn", + "startLine": 34, + "endLine": 42, + "tokens": 112 + }, + { + "name": "TestMessageWithNoMatchingRuleIsUntouched", + "kind": "fn", + "startLine": 44, + "endLine": 52, + "tokens": 119 + }, + { + "name": "TestNoScriptMeansNoDecision", + "kind": "fn", + "startLine": 55, + "endLine": 62, + "tokens": 104 + }, + { + "name": "TestABrokenScriptStillDeliversTheMessage", + "kind": "fn", + "startLine": 67, + "endLine": 74, + "tokens": 108 + }, + { + "name": "TestAFailingLookupStillDeliversTheMessage", + "kind": "fn", + "startLine": 76, + "endLine": 83, + "tokens": 108 + }, + { + "name": "TestVacationRepliesToAPerson", + "kind": "fn", + "startLine": 90, + "endLine": 101, + "tokens": 134 + }, + { + "name": "TestVacationStaysSilentForAList", + "kind": "fn", + "startLine": 105, + "endLine": 113, + "tokens": 113 + }, + { + "name": "TestVacationStaysSilentForABounce", + "kind": "fn", + "startLine": 115, + "endLine": 124, + "tokens": 120 + }, + { + "name": "TestRulesAreLookedUpForTheReceivingMailbox", + "kind": "fn", + "startLine": 127, + "endLine": 138, + "tokens": 122 + }, + { + "name": "TestDeliveryFilesWhereTheRuleSays", + "kind": "fn", + "startLine": 145, + "endLine": 165, + "tokens": 248 + }, + { + "name": "TestEachRecipientGetsTheirOwnRules", + "kind": "fn", + "startLine": 169, + "endLine": 190, + "tokens": 251 + }, + { + "name": "TestDiscardAcceptsTheMessageAndFilesNothing", + "kind": "fn", + "startLine": 194, + "endLine": 212, + "tokens": 229 + }, + { + "name": "TestDeliveryIsUnchangedWithoutRules", + "kind": "fn", + "startLine": 215, + "endLine": 230, + "tokens": 181 + }, + { + "name": "bytesReader", + "kind": "fn", + "startLine": 233, + "endLine": 233, + "tokens": 21 + } + ], + "symbolSource": "ts", + "skeleton": "type stubSieveScripts struct {\nfunc (s *stubSieveScripts) ActiveScriptFor(_ context.Context, mailboxID uuid.UUID) (string, error) {\nfunc newRules(scripts *stubSieveScripts) *ApplyRulesUseCase {\nfunc TestRuleDecidesTheFolder(t *testing.T) {\nfunc TestMessageWithNoMatchingRuleIsUntouched(t *testing.T) {\nfunc TestNoScriptMeansNoDecision(t *testing.T) {\nfunc TestABrokenScriptStillDeliversTheMessage(t *testing.T) {\nfunc TestAFailingLookupStillDeliversTheMessage(t *testing.T) {\nfunc TestVacationRepliesToAPerson(t *testing.T) {\nfunc TestVacationStaysSilentForAList(t *testing.T) {\nfunc TestVacationStaysSilentForABounce(t *testing.T) {\nfunc TestRulesAreLookedUpForTheReceivingMailbox(t *testing.T) {\nfunc TestDeliveryFilesWhereTheRuleSays(t *testing.T) {\nfunc TestEachRecipientGetsTheirOwnRules(t *testing.T) {\nfunc TestDiscardAcceptsTheMessageAndFilesNothing(t *testing.T) {\nfunc TestDeliveryIsUnchangedWithoutRules(t *testing.T) {\nfunc bytesReader(s string) io.Reader { return bytes.NewBufferString(s) }", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.1794 + }, + "services/protocols/internal/application/usecase/apply_rules.go": { + "description": "ApplyRulesUseCase (20 fields); methods: For", + "tokens": 990, + "hash": "0fbcc8babc85cc3c", + "size": 3465, + "mtimeMs": 1787420466489.285, + "updatedAt": "2026-10-06T11:06:20.862Z", + "source": "scan", + "symbols": [ + { + "name": "ApplyRulesUseCase", + "kind": "class", + "startLine": 22, + "endLine": 24, + "tokens": 19 + }, + { + "name": "NewApplyRulesUseCase", + "kind": "fn", + "startLine": 26, + "endLine": 28, + "tokens": 36 + }, + { + "name": "RuleDecision", + "kind": "class", + "startLine": 31, + "endLine": 42, + "tokens": 139 + }, + { + "name": "For", + "kind": "method", + "startLine": 50, + "endLine": 86, + "tokens": 299 + }, + { + "name": "headersOf", + "kind": "fn", + "startLine": 92, + "endLine": 102, + "tokens": 90 + }, + { + "name": "firstHeader", + "kind": "fn", + "startLine": 105, + "endLine": 111, + "tokens": 43 + } + ], + "symbolSource": "ts", + "imports": [ + "services/webmail/src/app/(user)/user/mail/[folder]/page.tsx", + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/domain/sieve/parser.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/backfill_reports_test.go": { + "description": "stubDeliveredReportStore (35 fields); methods: ListUningestedReports, MoveToReportsFolder, ReadByID", + "tokens": 1565, + "hash": "5497ef40f4d13fba", + "size": 5477, + "mtimeMs": 1787316127470.0635, + "updatedAt": "2026-10-06T11:06:20.862Z", + "source": "scan", + "symbols": [ + { + "name": "stubDeliveredReportStore", + "kind": "class", + "startLine": 17, + "endLine": 24, + "tokens": 50 + }, + { + "name": "ListUningestedReports", + "kind": "method", + "startLine": 26, + "endLine": 36, + "tokens": 80 + }, + { + "name": "MoveToReportsFolder", + "kind": "method", + "startLine": 38, + "endLine": 44, + "tokens": 57 + }, + { + "name": "ReadByID", + "kind": "method", + "startLine": 46, + "endLine": 55, + "tokens": 73 + }, + { + "name": "newBackfill", + "kind": "fn", + "startLine": 57, + "endLine": 60, + "tokens": 63 + }, + { + "name": "TestBackfillIngestsAReportAlreadyInTheMailbox", + "kind": "fn", + "startLine": 62, + "endLine": 93, + "tokens": 313 + }, + { + "name": "TestBackfillOverAnEmptyBacklogDoesNothing", + "kind": "fn", + "startLine": 97, + "endLine": 108, + "tokens": 103 + }, + { + "name": "TestBackfillContinuesPastAnUnreadableReport", + "kind": "fn", + "startLine": 113, + "endLine": 144, + "tokens": 340 + }, + { + "name": "TestBackfillSurvivesAMissingBlob", + "kind": "fn", + "startLine": 147, + "endLine": 163, + "tokens": 153 + }, + { + "name": "TestBackfillStopsIfItCannotReadTheBacklog", + "kind": "fn", + "startLine": 167, + "endLine": 172, + "tokens": 92 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/backfill_reports.go": { + "description": "BackfillReportsUseCase (16 fields); methods: Run", + "tokens": 846, + "hash": "43ab4b80f09dd18d", + "size": 2958, + "mtimeMs": 1787316113051.5327, + "updatedAt": "2026-10-06T11:06:20.862Z", + "source": "scan", + "symbols": [ + { + "name": "BackfillReportsUseCase", + "kind": "class", + "startLine": 19, + "endLine": 23, + "tokens": 41 + }, + { + "name": "NewBackfillReportsUseCase", + "kind": "fn", + "startLine": 25, + "endLine": 31, + "tokens": 69 + }, + { + "name": "BackfillSummary", + "kind": "class", + "startLine": 34, + "endLine": 42, + "tokens": 93 + }, + { + "name": "Run", + "kind": "method", + "startLine": 50, + "endLine": 89, + "tokens": 370 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/imap_session_test.go": { + "description": "fakeAuthRepository (40 fields); methods: FindByAddress, ListFolders, FindByName, Expunge", + "tokens": 1828, + "hash": "ddaeebae69cda399", + "size": 6395, + "mtimeMs": 1785635482522.9756, + "updatedAt": "2026-10-06T11:06:20.862Z", + "source": "scan", + "symbols": [ + { + "name": "fakeAuthRepository", + "kind": "class", + "startLine": 17, + "endLine": 19, + "tokens": 21 + }, + { + "name": "FindByAddress", + "kind": "method", + "startLine": 21, + "endLine": 27, + "tokens": 55 + }, + { + "name": "TestImapSessionUseCase_Login_SucceedsForCorrectPassword", + "kind": "fn", + "startLine": 29, + "endLine": 44, + "tokens": 186 + }, + { + "name": "TestImapSessionUseCase_Login_FailsForWrongPassword", + "kind": "fn", + "startLine": 46, + "endLine": 56, + "tokens": 131 + }, + { + "name": "TestImapSessionUseCase_Login_FailsForUnknownAddress", + "kind": "fn", + "startLine": 58, + "endLine": 66, + "tokens": 127 + }, + { + "name": "fakeImapFolderRepository", + "kind": "class", + "startLine": 68, + "endLine": 70, + "tokens": 24 + }, + { + "name": "ListFolders", + "kind": "method", + "startLine": 72, + "endLine": 78, + "tokens": 64 + }, + { + "name": "FindByName", + "kind": "method", + "startLine": 80, + "endLine": 86, + "tokens": 57 + }, + { + "name": "TestImapSessionUseCase_SelectFolder_ReturnsRecordForExistingFolder", + "kind": "fn", + "startLine": 88, + "endLine": 101, + "tokens": 165 + }, + { + "name": "TestImapSessionUseCase_SelectFolder_ReturnsErrNoSuchFolderWhenMissing", + "kind": "fn", + "startLine": 103, + "endLine": 111, + "tokens": 119 + }, + { + "name": "fakeExpungeRepository", + "kind": "class", + "startLine": 113, + "endLine": 118, + "tokens": 28 + }, + { + "name": "Expunge", + "kind": "method", + "startLine": 120, + "endLine": 126, + "tokens": 59 + }, + { + "name": "fakeCopyRepository", + "kind": "class", + "startLine": 128, + "endLine": 130, + "tokens": 18 + }, + { + "name": "CopyMessages", + "kind": "method", + "startLine": 132, + "endLine": 134, + "tokens": 43 + }, + { + "name": "TestImapSessionUseCase_Expunge_DelegatesToRepository", + "kind": "fn", + "startLine": 136, + "endLine": 150, + "tokens": 183 + }, + { + "name": "fakeFlagRepository", + "kind": "class", + "startLine": 152, + "endLine": 158, + "tokens": 51 + }, + { + "name": "SetFlags", + "kind": "method", + "startLine": 160, + "endLine": 163, + "tokens": 84 + }, + { + "name": "TestImapSessionUseCase_SetFlags_DelegatesToRepository", + "kind": "fn", + "startLine": 165, + "endLine": 176, + "tokens": 163 + }, + { + "name": "TestImapSessionUseCase_CopyMessages_ReturnsRepositoryResult", + "kind": "fn", + "startLine": 178, + "endLine": 189, + "tokens": 156 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/imap_session.go": { + "description": "ImapSessionUseCase (25 fields); methods: SetTrackerManager, GetTrackerManager, Login, ListFolders", + "tokens": 1085, + "hash": "4742bc5de6265257", + "size": 3795, + "mtimeMs": 1785635482522.3005, + "updatedAt": "2026-10-06T11:06:20.862Z", + "source": "scan", + "symbols": [ + { + "name": "ImapSessionUseCase", + "kind": "class", + "startLine": 25, + "endLine": 34, + "tokens": 97 + }, + { + "name": "NewImapSessionUseCase", + "kind": "fn", + "startLine": 36, + "endLine": 47, + "tokens": 149 + }, + { + "name": "SetTrackerManager", + "kind": "method", + "startLine": 49, + "endLine": 53, + "tokens": 35 + }, + { + "name": "GetTrackerManager", + "kind": "method", + "startLine": 55, + "endLine": 57, + "tokens": 30 + }, + { + "name": "Login", + "kind": "method", + "startLine": 59, + "endLine": 75, + "tokens": 120 + }, + { + "name": "ListFolders", + "kind": "method", + "startLine": 77, + "endLine": 79, + "tokens": 47 + }, + { + "name": "SelectFolder", + "kind": "method", + "startLine": 81, + "endLine": 90, + "tokens": 82 + }, + { + "name": "FetchMessages", + "kind": "method", + "startLine": 92, + "endLine": 94, + "tokens": 47 + }, + { + "name": "ReadBlob", + "kind": "method", + "startLine": 96, + "endLine": 98, + "tokens": 39 + }, + { + "name": "SetFlags", + "kind": "method", + "startLine": 100, + "endLine": 102, + "tokens": 67 + }, + { + "name": "Expunge", + "kind": "method", + "startLine": 104, + "endLine": 106, + "tokens": 43 + }, + { + "name": "CopyMessages", + "kind": "method", + "startLine": 108, + "endLine": 110, + "tokens": 66 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/inbound_auth_test.go": { + "description": "stubAuthenticator (19 fields); methods: Authenticate", + "tokens": 1438, + "hash": "faa083441706756a", + "size": 5031, + "mtimeMs": 1785635482522.9756, + "updatedAt": "2026-10-06T11:06:20.862Z", + "source": "scan", + "symbols": [ + { + "name": "stubAuthenticator", + "kind": "class", + "startLine": 15, + "endLine": 18, + "tokens": 27 + }, + { + "name": "Authenticate", + "kind": "method", + "startLine": 20, + "endLine": 23, + "tokens": 43 + }, + { + "name": "inboundInput", + "kind": "fn", + "startLine": 25, + "endLine": 35, + "tokens": 116 + }, + { + "name": "TestUseCase_Handle_PersistsAuthenticationVerdicts", + "kind": "fn", + "startLine": 39, + "endLine": 54, + "tokens": 198 + }, + { + "name": "TestUseCase_Handle_PrependsAuthenticationResultsHeader", + "kind": "fn", + "startLine": 58, + "endLine": 73, + "tokens": 206 + }, + { + "name": "TestUseCase_Handle_RejectsOnDmarcFailWithRejectPolicy", + "kind": "fn", + "startLine": 77, + "endLine": 95, + "tokens": 204 + }, + { + "name": "TestUseCase_Handle_AcceptsOnDmarcFailWithQuarantinePolicy", + "kind": "fn", + "startLine": 99, + "endLine": 112, + "tokens": 165 + }, + { + "name": "TestUseCase_Handle_PassesPeerIdentityToAuthenticator", + "kind": "fn", + "startLine": 116, + "endLine": 138, + "tokens": 239 + }, + { + "name": "parseTestIP", + "kind": "fn", + "startLine": 140, + "endLine": 140, + "tokens": 17 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/inbound_scan_headers_test.go": { + "description": "capturingBlobStorage (9 fields); methods: Store, Scan", + "tokens": 882, + "hash": "7a5ef06b82c637c2", + "size": 3087, + "mtimeMs": 1785635482522.9756, + "updatedAt": "2026-10-06T11:06:20.863Z", + "source": "scan", + "symbols": [ + { + "name": "capturingBlobStorage", + "kind": "class", + "startLine": 16, + "endLine": 18, + "tokens": 15 + }, + { + "name": "Store", + "kind": "method", + "startLine": 20, + "endLine": 24, + "tokens": 58 + }, + { + "name": "stubScanner", + "kind": "class", + "startLine": 26, + "endLine": 28, + "tokens": 17 + }, + { + "name": "Scan", + "kind": "method", + "startLine": 30, + "endLine": 32, + "tokens": 35 + }, + { + "name": "TestUseCase_Handle_WritesScanHeadersIntoStoredMessage", + "kind": "fn", + "startLine": 38, + "endLine": 80, + "tokens": 430 + }, + { + "name": "TestUseCase_Handle_LeavesMessageUntouchedWithoutScanHeaders", + "kind": "fn", + "startLine": 82, + "endLine": 99, + "tokens": 189 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/inbound_system_message_test.go": { + "description": "junkingScanner (7 fields); methods: Scan", + "tokens": 1144, + "hash": "7fee051806b3a830", + "size": 4004, + "mtimeMs": 1785711428684.0837, + "updatedAt": "2026-10-06T11:06:20.863Z", + "source": "scan", + "symbols": [ + { + "name": "junkingScanner", + "kind": "class", + "startLine": 17, + "endLine": 17, + "tokens": 12 + }, + { + "name": "Scan", + "kind": "method", + "startLine": 19, + "endLine": 27, + "tokens": 88 + }, + { + "name": "TestSystemGeneratedMessageIsNotSpamFiltered", + "kind": "fn", + "startLine": 37, + "endLine": 65, + "tokens": 316 + }, + { + "name": "TestOrdinaryMessageIsStillScanned", + "kind": "fn", + "startLine": 70, + "endLine": 94, + "tokens": 274 + }, + { + "name": "TestDsnDeclaresItselfAutoGenerated", + "kind": "fn", + "startLine": 99, + "endLine": 110, + "tokens": 125 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/ingest_delivered_reports_test.go": { + "description": "recordingReportRepo (48 fields); methods: SaveDmarcReport, SaveTlsRptReport", + "tokens": 3334, + "hash": "8ebf034ccec5fbd5", + "size": 11668, + "mtimeMs": 1787316127469.6414, + "updatedAt": "2026-10-06T11:06:20.863Z", + "source": "scan", + "symbols": [ + { + "name": "recordingReportRepo", + "kind": "class", + "startLine": 21, + "endLine": 25, + "tokens": 32 + }, + { + "name": "SaveDmarcReport", + "kind": "method", + "startLine": 27, + "endLine": 33, + "tokens": 54 + }, + { + "name": "SaveTlsRptReport", + "kind": "method", + "startLine": 35, + "endLine": 41, + "tokens": 55 + }, + { + "name": "gzipped", + "kind": "fn", + "startLine": 44, + "endLine": 55, + "tokens": 75 + }, + { + "name": "messageWithAttachment", + "kind": "fn", + "startLine": 59, + "endLine": 80, + "tokens": 276 + }, + { + "name": "newReportIngestor", + "kind": "fn", + "startLine": 91, + "endLine": 93, + "tokens": 45 + }, + { + "name": "TestTlsRptReportAddressedToTlsRptIsIngested", + "kind": "fn", + "startLine": 95, + "endLine": 114, + "tokens": 190 + }, + { + "name": "TestDmarcReportAddressedToDmarcIsIngested", + "kind": "fn", + "startLine": 116, + "endLine": 134, + "tokens": 173 + }, + { + "name": "TestOrdinaryMailIsNotTreatedAsAReport", + "kind": "fn", + "startLine": 138, + "endLine": 154, + "tokens": 168 + }, + { + "name": "TestUnparseableReportIsRecognisedButNeverFailsDelivery", + "kind": "fn", + "startLine": 159, + "endLine": 175, + "tokens": 180 + }, + { + "name": "TestStorageFailureDoesNotFailDelivery", + "kind": "fn", + "startLine": 179, + "endLine": 188, + "tokens": 124 + }, + { + "name": "TestReportAddressMatchingIsCaseInsensitive", + "kind": "fn", + "startLine": 192, + "endLine": 201, + "tokens": 118 + }, + { + "name": "TestReportWithNoAttachmentDoesNotFail", + "kind": "fn", + "startLine": 205, + "endLine": 217, + "tokens": 123 + }, + { + "name": "TestDeliveredReportIsIngestedAndFiledInReports", + "kind": "fn", + "startLine": 242, + "endLine": 273, + "tokens": 374 + }, + { + "name": "TestOrdinaryMailStillReachesTheInbox", + "kind": "fn", + "startLine": 276, + "endLine": 298, + "tokens": 237 + }, + { + "name": "TestDeliveryIsUnchangedWithoutAnIngestor", + "kind": "fn", + "startLine": 302, + "endLine": 318, + "tokens": 216 + } + ], + "symbolSource": "ts", + "skeleton": "type recordingReportRepo struct {\nfunc (r *recordingReportRepo) SaveDmarcReport(_ context.Context, report *entity.DmarcReport) error {\nfunc (r *recordingReportRepo) SaveTlsRptReport(_ context.Context, report *entity.TlsRptReport) error {\nfunc gzipped(t *testing.T, payload string) []byte {\nfunc messageWithAttachment(filename, contentType string, content []byte) []byte {\nfunc newReportIngestor(repo *recordingReportRepo) *IngestDeliveredReportsUseCase {\nfunc TestTlsRptReportAddressedToTlsRptIsIngested(t *testing.T) {\nfunc TestDmarcReportAddressedToDmarcIsIngested(t *testing.T) {\nfunc TestOrdinaryMailIsNotTreatedAsAReport(t *testing.T) {\nfunc TestUnparseableReportIsRecognisedButNeverFailsDelivery(t *testing.T) {\nfunc TestStorageFailureDoesNotFailDelivery(t *testing.T) {\nfunc TestReportAddressMatchingIsCaseInsensitive(t *testing.T) {\nfunc TestReportWithNoAttachmentDoesNotFail(t *testing.T) {\nfunc TestDeliveredReportIsIngestedAndFiledInReports(t *testing.T) {\nfunc TestOrdinaryMailStillReachesTheInbox(t *testing.T) {\nfunc TestDeliveryIsUnchangedWithoutAnIngestor(t *testing.T) {", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/ingest_delivered_reports.go": { + "description": "IngestDeliveredReportsUseCase (34 fields); methods: Ingest", + "tokens": 1905, + "hash": "4adc9bc0c6621d1a", + "size": 6666, + "mtimeMs": 1787316113038.0015, + "updatedAt": "2026-10-06T11:06:20.864Z", + "source": "scan", + "symbols": [ + { + "name": "ReportKind", + "kind": "class", + "startLine": 15, + "endLine": 15, + "tokens": 7 + }, + { + "name": "IngestDeliveredReportsUseCase", + "kind": "class", + "startLine": 51, + "endLine": 53, + "tokens": 22 + }, + { + "name": "NewIngestDeliveredReportsUseCase", + "kind": "fn", + "startLine": 55, + "endLine": 57, + "tokens": 46 + }, + { + "name": "Ingest", + "kind": "method", + "startLine": 68, + "endLine": 114, + "tokens": 470 + }, + { + "name": "ReportOutcome", + "kind": "class", + "startLine": 123, + "endLine": 126, + "tokens": 18 + }, + { + "name": "reportKindForAddress", + "kind": "fn", + "startLine": 129, + "endLine": 135, + "tokens": 60 + }, + { + "name": "reportPayloads", + "kind": "fn", + "startLine": 145, + "endLine": 153, + "tokens": 67 + }, + { + "name": "collectReportParts", + "kind": "fn", + "startLine": 163, + "endLine": 188, + "tokens": 218 + } + ], + "symbolSource": "ts", + "imports": [ + "services/webmail/src/app/(admin)/admin/dmarc/page.tsx" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/ingest_reports_test.go": { + "description": "fakeReportRepository (8 fields); methods: SaveDmarcReport, SaveTlsRptReport", + "tokens": 612, + "hash": "036dc9128d2ac27a", + "size": 2140, + "mtimeMs": 1785635482522.9756, + "updatedAt": "2026-10-06T11:06:20.864Z", + "source": "scan", + "symbols": [ + { + "name": "fakeReportRepository", + "kind": "class", + "startLine": 10, + "endLine": 13, + "tokens": 32 + }, + { + "name": "SaveDmarcReport", + "kind": "method", + "startLine": 15, + "endLine": 18, + "tokens": 47 + }, + { + "name": "SaveTlsRptReport", + "kind": "method", + "startLine": 20, + "endLine": 23, + "tokens": 48 + }, + { + "name": "TestIngestReportsUseCase_IngestDmarcAndTlsRpt", + "kind": "fn", + "startLine": 25, + "endLine": 62, + "tokens": 455 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/ingest_reports.go": { + "description": "IngestReportsUseCase (15 fields); methods: IngestDmarc, IngestTlsRpt", + "tokens": 347, + "hash": "a6426e2f0646755f", + "size": 1213, + "mtimeMs": 1785635482522.9756, + "updatedAt": "2026-10-06T11:06:20.864Z", + "source": "scan", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/mailbox_tracker_manager.go": { + "description": "MailboxTrackerManager (9 fields); methods: GetTracker, NotifyNumMessages, NotifyMessageFlags, NotifyExpunge", + "tokens": 585, + "hash": "0a9120b2d647b28e", + "size": 2046, + "mtimeMs": 1785635482522.9756, + "updatedAt": "2026-10-06T11:06:20.864Z", + "source": "scan", + "symbols": [ + { + "name": "MailboxTrackerManager", + "kind": "class", + "startLine": 13, + "endLine": 16, + "tokens": 31 + }, + { + "name": "NewMailboxTrackerManager", + "kind": "fn", + "startLine": 18, + "endLine": 22, + "tokens": 43 + }, + { + "name": "GetTracker", + "kind": "method", + "startLine": 25, + "endLine": 35, + "tokens": 90 + }, + { + "name": "NotifyNumMessages", + "kind": "method", + "startLine": 38, + "endLine": 46, + "tokens": 55 + }, + { + "name": "NotifyMessageFlags", + "kind": "method", + "startLine": 49, + "endLine": 61, + "tokens": 117 + }, + { + "name": "NotifyExpunge", + "kind": "method", + "startLine": 64, + "endLine": 72, + "tokens": 55 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/manage_folders_test.go": { + "description": "stubFolderStore (47 fields); methods: ListFolders, CreateFolder, RenameFolder, DeleteFolder", + "tokens": 2128, + "hash": "0dfb90d34e47fda2", + "size": 7448, + "mtimeMs": 1787417842590.568, + "updatedAt": "2026-10-06T11:06:20.864Z", + "source": "scan", + "symbols": [ + { + "name": "stubFolderStore", + "kind": "class", + "startLine": 20, + "endLine": 26, + "tokens": 39 + }, + { + "name": "ListFolders", + "kind": "method", + "startLine": 28, + "endLine": 30, + "tokens": 35 + }, + { + "name": "CreateFolder", + "kind": "method", + "startLine": 32, + "endLine": 39, + "tokens": 67 + }, + { + "name": "RenameFolder", + "kind": "method", + "startLine": 41, + "endLine": 47, + "tokens": 52 + }, + { + "name": "DeleteFolder", + "kind": "method", + "startLine": 49, + "endLine": 55, + "tokens": 49 + }, + { + "name": "newFolders", + "kind": "fn", + "startLine": 57, + "endLine": 59, + "tokens": 44 + }, + { + "name": "standardFolders", + "kind": "fn", + "startLine": 61, + "endLine": 68, + "tokens": 69 + }, + { + "name": "TestCreatesAFolder", + "kind": "fn", + "startLine": 70, + "endLine": 78, + "tokens": 97 + }, + { + "name": "TestFolderNameIsTrimmed", + "kind": "fn", + "startLine": 80, + "endLine": 88, + "tokens": 100 + }, + { + "name": "TestRefusesAnEmptyName", + "kind": "fn", + "startLine": 90, + "endLine": 97, + "tokens": 96 + }, + { + "name": "TestRefusesADuplicateName", + "kind": "fn", + "startLine": 101, + "endLine": 109, + "tokens": 114 + }, + { + "name": "TestDuplicateCheckIgnoresCase", + "kind": "fn", + "startLine": 111, + "endLine": 116, + "tokens": 89 + }, + { + "name": "TestRefusesToTouchTheStandardFolders", + "kind": "fn", + "startLine": 121, + "endLine": 136, + "tokens": 210 + }, + { + "name": "TestRefusesToTouchTheReportsFolder", + "kind": "fn", + "startLine": 140, + "endLine": 145, + "tokens": 91 + }, + { + "name": "TestRenamesACustomFolder", + "kind": "fn", + "startLine": 147, + "endLine": 155, + "tokens": 119 + }, + { + "name": "TestRenameRefusesAnExistingName", + "kind": "fn", + "startLine": 157, + "endLine": 163, + "tokens": 106 + }, + { + "name": "TestDeletesACustomFolder", + "kind": "fn", + "startLine": 165, + "endLine": 173, + "tokens": 112 + }, + { + "name": "TestDeletingAFolderThatIsNotThereIsReported", + "kind": "fn", + "startLine": 175, + "endLine": 180, + "tokens": 81 + }, + { + "name": "TestRefusesADelimiterInTheName", + "kind": "fn", + "startLine": 184, + "endLine": 191, + "tokens": 99 + }, + { + "name": "TestRefusesAnAbsurdlyLongName", + "kind": "fn", + "startLine": 193, + "endLine": 199, + "tokens": 82 + }, + { + "name": "TestAcceptsAccentedNames", + "kind": "fn", + "startLine": 202, + "endLine": 207, + "tokens": 81 + } + ], + "symbolSource": "ts", + "skeleton": "type stubFolderStore struct {\nfunc (s *stubFolderStore) ListFolders(context.Context, string) ([]port.WebmailFolder, error) {\nfunc (s *stubFolderStore) CreateFolder(_ context.Context, _, name string) error {\nfunc (s *stubFolderStore) RenameFolder(_ context.Context, _, from, to string) error {\nfunc (s *stubFolderStore) DeleteFolder(_ context.Context, _, name string) error {\nfunc newFolders(store *stubFolderStore) *ManageFoldersUseCase {\nfunc standardFolders() []port.WebmailFolder {\nfunc TestCreatesAFolder(t *testing.T) {\nfunc TestFolderNameIsTrimmed(t *testing.T) {\nfunc TestRefusesAnEmptyName(t *testing.T) {\nfunc TestRefusesADuplicateName(t *testing.T) {\nfunc TestDuplicateCheckIgnoresCase(t *testing.T) {\nfunc TestRefusesToTouchTheStandardFolders(t *testing.T) {\nfunc TestRefusesToTouchTheReportsFolder(t *testing.T) {\nfunc TestRenamesACustomFolder(t *testing.T) {\nfunc TestRenameRefusesAnExistingName(t *testing.T) {\nfunc TestDeletesACustomFolder(t *testing.T) {\nfunc TestDeletingAFolderThatIsNotThereIsReported(t *testing.T) {\nfunc TestRefusesADelimiterInTheName(t *testing.T) {\nfunc TestRefusesAnAbsurdlyLongName(t *testing.T) {\nfunc TestAcceptsAccentedNames(t *testing.T) {", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/manage_folders.go": { + "description": "ManageFoldersUseCase (52 fields); methods: Create, Rename, Delete", + "tokens": 1304, + "hash": "b46de406e4735cf9", + "size": 4564, + "mtimeMs": 1787416262306.497, + "updatedAt": "2026-10-06T11:06:20.864Z", + "source": "scan", + "symbols": [ + { + "name": "ManageFoldersUseCase", + "kind": "class", + "startLine": 16, + "endLine": 19, + "tokens": 27 + }, + { + "name": "NewManageFoldersUseCase", + "kind": "fn", + "startLine": 21, + "endLine": 23, + "tokens": 51 + }, + { + "name": "Create", + "kind": "method", + "startLine": 47, + "endLine": 65, + "tokens": 137 + }, + { + "name": "Rename", + "kind": "method", + "startLine": 67, + "endLine": 92, + "tokens": 188 + }, + { + "name": "Delete", + "kind": "method", + "startLine": 98, + "endLine": 116, + "tokens": 140 + }, + { + "name": "mailbox", + "kind": "method", + "startLine": 118, + "endLine": 124, + "tokens": 67 + }, + { + "name": "findFolder", + "kind": "fn", + "startLine": 126, + "endLine": 133, + "tokens": 63 + }, + { + "name": "validateFolderName", + "kind": "fn", + "startLine": 136, + "endLine": 158, + "tokens": 184 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/managesieve_session_test.go": { + "description": "fakeSieveRepository (27 fields); methods: GetScript, PutScript, ListScripts, SetActiveScript", + "tokens": 1114, + "hash": "f93f3de9bc29d4f1", + "size": 3899, + "mtimeMs": 1785635482522.9756, + "updatedAt": "2026-10-06T11:06:20.864Z", + "source": "scan", + "symbols": [ + { + "name": "fakeSieveRepository", + "kind": "class", + "startLine": 12, + "endLine": 14, + "tokens": 35 + }, + { + "name": "newFakeSieveRepository", + "kind": "fn", + "startLine": 16, + "endLine": 20, + "tokens": 43 + }, + { + "name": "GetScript", + "kind": "method", + "startLine": 22, + "endLine": 32, + "tokens": 72 + }, + { + "name": "PutScript", + "kind": "method", + "startLine": 34, + "endLine": 53, + "tokens": 140 + }, + { + "name": "ListScripts", + "kind": "method", + "startLine": 55, + "endLine": 65, + "tokens": 82 + }, + { + "name": "SetActiveScript", + "kind": "method", + "startLine": 67, + "endLine": 77, + "tokens": 69 + }, + { + "name": "DeleteScript", + "kind": "method", + "startLine": 79, + "endLine": 86, + "tokens": 53 + }, + { + "name": "RenameScript", + "kind": "method", + "startLine": 88, + "endLine": 98, + "tokens": 74 + }, + { + "name": "TestManageSieveSessionUseCase_Put_Get_List_SetActive_Delete", + "kind": "fn", + "startLine": 100, + "endLine": 150, + "tokens": 506 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/managesieve_session.go": { + "description": "ManageSieveSessionUseCase (45 fields); methods: Login, PutScript, GetScript, ListScripts", + "tokens": 1011, + "hash": "961cc33ac2e226bc", + "size": 3536, + "mtimeMs": 1785635482522.9756, + "updatedAt": "2026-10-06T11:06:20.864Z", + "source": "scan", + "symbols": [ + { + "name": "ManageSieveSessionUseCase", + "kind": "class", + "startLine": 23, + "endLine": 26, + "tokens": 30 + }, + { + "name": "NewManageSieveSessionUseCase", + "kind": "fn", + "startLine": 28, + "endLine": 33, + "tokens": 61 + }, + { + "name": "Login", + "kind": "method", + "startLine": 35, + "endLine": 48, + "tokens": 126 + }, + { + "name": "PutScript", + "kind": "method", + "startLine": 50, + "endLine": 58, + "tokens": 106 + }, + { + "name": "GetScript", + "kind": "method", + "startLine": 60, + "endLine": 69, + "tokens": 84 + }, + { + "name": "ListScripts", + "kind": "method", + "startLine": 71, + "endLine": 73, + "tokens": 50 + }, + { + "name": "SetActive", + "kind": "method", + "startLine": 75, + "endLine": 86, + "tokens": 93 + }, + { + "name": "DeleteScript", + "kind": "method", + "startLine": 88, + "endLine": 100, + "tokens": 101 + }, + { + "name": "RenameScript", + "kind": "method", + "startLine": 102, + "endLine": 120, + "tokens": 139 + }, + { + "name": "CheckScript", + "kind": "method", + "startLine": 122, + "endLine": 127, + "tokens": 61 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/message_headers_test.go": { + "description": "TestExtractMessageHeaders_ReadsSubjectSenderAndSnippet, TestExtractMessageHeaders_DecodesEncodedWords, TestExtractMessageHeaders_FlagsMultipartMixe...", + "tokens": 1604, + "hash": "5a1a9c8fd01baac6", + "size": 5612, + "mtimeMs": 1787584828278.8896, + "updatedAt": "2026-10-06T11:06:20.865Z", + "source": "scan", + "symbols": [ + { + "name": "TestExtractMessageHeaders_ReadsSubjectSenderAndSnippet", + "kind": "fn", + "startLine": 5, + "endLine": 31, + "tokens": 260 + }, + { + "name": "TestExtractMessageHeaders_DecodesEncodedWords", + "kind": "fn", + "startLine": 35, + "endLine": 48, + "tokens": 137 + }, + { + "name": "TestExtractMessageHeaders_FlagsMultipartMixedAsAttachment", + "kind": "fn", + "startLine": 50, + "endLine": 63, + "tokens": 201 + }, + { + "name": "TestExtractMessageHeaders_SurvivesGarbage", + "kind": "fn", + "startLine": 67, + "endLine": 72, + "tokens": 78 + }, + { + "name": "TestExtractMessageHeaders_TruncatesWithoutBreakingUTF8", + "kind": "fn", + "startLine": 74, + "endLine": 88, + "tokens": 124 + }, + { + "name": "TestExtractMessageHeaders_SinglePartAttachmentCounts", + "kind": "fn", + "startLine": 95, + "endLine": 110, + "tokens": 177 + }, + { + "name": "TestExtractMessageHeaders_AttachmentDetection", + "kind": "fn", + "startLine": 112, + "endLine": 144, + "tokens": 434 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/message_headers.go": { + "description": "MessageHeaders (33 fields)", + "tokens": 1388, + "hash": "81ae8a3cc636e2c6", + "size": 4858, + "mtimeMs": 1787584848508.4407, + "updatedAt": "2026-10-06T11:06:20.865Z", + "source": "scan", + "symbols": [ + { + "name": "MessageHeaders", + "kind": "class", + "startLine": 18, + "endLine": 24, + "tokens": 43 + }, + { + "name": "decodeHeader", + "kind": "fn", + "startLine": 35, + "endLine": 41, + "tokens": 41 + }, + { + "name": "ExtractMessageHeaders", + "kind": "fn", + "startLine": 48, + "endLine": 80, + "tokens": 308 + }, + { + "name": "carriesAttachment", + "kind": "fn", + "startLine": 90, + "endLine": 117, + "tokens": 285 + }, + { + "name": "buildSnippet", + "kind": "fn", + "startLine": 120, + "endLine": 138, + "tokens": 166 + }, + { + "name": "truncateUTF8", + "kind": "fn", + "startLine": 142, + "endLine": 151, + "tokens": 53 + } + ], + "symbolSource": "ts", + "imports": [ + "services/webmail/src/app/(user)/user/mail/[folder]/page.tsx" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/message_render_test.go": { + "description": "TestRenderMessage_ExtractsHeadersAndText, TestRenderMessage_LeavesDateEmptyWhenHeaderAbsent, TestRenderMessage_PrefersPlainTextAndListsAttachments,...", + "tokens": 1976, + "hash": "c0e29f8c7f65037a", + "size": 6916, + "mtimeMs": 1787075616725.4102, + "updatedAt": "2026-10-06T11:06:20.865Z", + "source": "scan", + "symbols": [ + { + "name": "TestRenderMessage_ExtractsHeadersAndText", + "kind": "fn", + "startLine": 10, + "endLine": 27, + "tokens": 161 + }, + { + "name": "TestRenderMessage_LeavesDateEmptyWhenHeaderAbsent", + "kind": "fn", + "startLine": 31, + "endLine": 36, + "tokens": 74 + }, + { + "name": "TestRenderMessage_PrefersPlainTextAndListsAttachments", + "kind": "fn", + "startLine": 38, + "endLine": 56, + "tokens": 212 + }, + { + "name": "TestRenderMessage_FallsBackToRawOnParseFailure", + "kind": "fn", + "startLine": 59, + "endLine": 64, + "tokens": 60 + }, + { + "name": "TestRenderMessage_ReturnsInlineImagesKeyedByContentID", + "kind": "fn", + "startLine": 70, + "endLine": 90, + "tokens": 248 + }, + { + "name": "TestRenderMessage_DoesNotListInlineImagesAsAttachments", + "kind": "fn", + "startLine": 94, + "endLine": 105, + "tokens": 164 + }, + { + "name": "TestRenderMessage_ListsNonImageInlinePartsAsAttachments", + "kind": "fn", + "startLine": 110, + "endLine": 121, + "tokens": 162 + }, + { + "name": "TestRenderMessage_SkipsOversizedInlineImages", + "kind": "fn", + "startLine": 125, + "endLine": 138, + "tokens": 166 + }, + { + "name": "TestRenderMessage_RefusesToInlineSvg", + "kind": "fn", + "startLine": 142, + "endLine": 151, + "tokens": 137 + }, + { + "name": "TestExtractAttachment_FindsInlineNamedParts", + "kind": "fn", + "startLine": 156, + "endLine": 171, + "tokens": 182 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/message_render.go": { + "description": "RenderedMessage (43 fields)", + "tokens": 2160, + "hash": "f4fa5b39a52cf212", + "size": 7560, + "mtimeMs": 1787075616725.3005, + "updatedAt": "2026-10-06T11:06:20.865Z", + "source": "scan", + "symbols": [ + { + "name": "RenderedMessage", + "kind": "class", + "startLine": 27, + "endLine": 46, + "tokens": 249 + }, + { + "name": "RenderMessage", + "kind": "fn", + "startLine": 55, + "endLine": 91, + "tokens": 319 + }, + { + "name": "collectParts", + "kind": "fn", + "startLine": 95, + "endLine": 148, + "tokens": 450 + }, + { + "name": "isInlineImageCandidate", + "kind": "fn", + "startLine": 154, + "endLine": 162, + "tokens": 81 + }, + { + "name": "collectInlineImage", + "kind": "fn", + "startLine": 168, + "endLine": 184, + "tokens": 187 + }, + { + "name": "ExtractAttachment", + "kind": "fn", + "startLine": 187, + "endLine": 216, + "tokens": 243 + } + ], + "symbolSource": "ts", + "skeleton": "type RenderedMessage struct {\nfunc RenderMessage(raw []byte, uid uint32) RenderedMessage {\nfunc collectParts(entity *message.Entity, out *RenderedMessage) {\nfunc isInlineImageCandidate(disposition, mediaType, contentID string) bool {\nfunc collectInlineImage(entity *message.Entity, mediaType string, out *RenderedMessage) bool {\nfunc ExtractAttachment(entity *message.Entity, targetName string) ([]byte, string) {", + "imports": [ + "services/webmail/src/app/(user)/user/mail/[folder]/page.tsx" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/outbound_dsn_test.go": { + "description": "TestOutboundWorker_PermanentSmtpFailureBouncesImmediately, TestOutboundWorker_TemporarySmtpFailureIsDeferred, TestOutboundWorker_DsnToFanOutSenderD...", + "tokens": 1480, + "hash": "cdca018b31782d2d", + "size": 5178, + "mtimeMs": 1785712539753.1628, + "updatedAt": "2026-10-06T11:06:20.866Z", + "source": "scan", + "symbols": [ + { + "name": "startFakeMX", + "kind": "fn", + "startLine": 19, + "endLine": 61, + "tokens": 291 + }, + { + "name": "newTestJob", + "kind": "fn", + "startLine": 63, + "endLine": 75, + "tokens": 119 + }, + { + "name": "TestOutboundWorker_PermanentSmtpFailureBouncesImmediately", + "kind": "fn", + "startLine": 79, + "endLine": 103, + "tokens": 218 + }, + { + "name": "TestOutboundWorker_TemporarySmtpFailureIsDeferred", + "kind": "fn", + "startLine": 106, + "endLine": 130, + "tokens": 215 + }, + { + "name": "TestOutboundWorker_DsnToFanOutSenderDeliversToEveryTarget", + "kind": "fn", + "startLine": 136, + "endLine": 170, + "tokens": 388 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/outbound_local_delivery_test.go": { + "description": "countingMXResolver (9 fields); methods: LookupMX", + "tokens": 1114, + "hash": "a5f716848dd365b6", + "size": 3896, + "mtimeMs": 1785712472154.4202, + "updatedAt": "2026-10-06T11:06:20.866Z", + "source": "scan", + "symbols": [ + { + "name": "countingMXResolver", + "kind": "class", + "startLine": 15, + "endLine": 15, + "tokens": 13 + }, + { + "name": "LookupMX", + "kind": "method", + "startLine": 17, + "endLine": 22, + "tokens": 75 + }, + { + "name": "newLocalDeliveryJob", + "kind": "fn", + "startLine": 24, + "endLine": 37, + "tokens": 126 + }, + { + "name": "TestLocalRecipientIsDeliveredWithoutLeavingTheHost", + "kind": "fn", + "startLine": 44, + "endLine": 82, + "tokens": 409 + }, + { + "name": "TestRemoteRecipientStillGoesOutOverSmtp", + "kind": "fn", + "startLine": 86, + "endLine": 114, + "tokens": 281 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/outbound_tls_policy_test.go": { + "description": "stubPolicyResolver (29 fields); methods: Resolve", + "tokens": 1582, + "hash": "2238831b4f9cb720", + "size": 5537, + "mtimeMs": 1785635482522.9756, + "updatedAt": "2026-10-06T11:06:20.866Z", + "source": "scan", + "symbols": [ + { + "name": "startPlaintextMX", + "kind": "fn", + "startLine": 17, + "endLine": 70, + "tokens": 364 + }, + { + "name": "stubPolicyResolver", + "kind": "class", + "startLine": 72, + "endLine": 75, + "tokens": 21 + }, + { + "name": "Resolve", + "kind": "method", + "startLine": 77, + "endLine": 79, + "tokens": 37 + }, + { + "name": "runWorkerWithPolicy", + "kind": "fn", + "startLine": 81, + "endLine": 99, + "tokens": 161 + }, + { + "name": "TestOutboundWorker_DefersRatherThanDowngradeUnderEnforcePolicy", + "kind": "fn", + "startLine": 104, + "endLine": 123, + "tokens": 189 + }, + { + "name": "TestOutboundWorker_DeliversOpportunisticallyWithoutPolicy", + "kind": "fn", + "startLine": 127, + "endLine": 140, + "tokens": 140 + }, + { + "name": "TestOutboundWorker_RefusesMxOutsidePolicy", + "kind": "fn", + "startLine": 144, + "endLine": 157, + "tokens": 142 + }, + { + "name": "TestOutboundWorker_DefersWhenPolicyCannotBeResolved", + "kind": "fn", + "startLine": 161, + "endLine": 174, + "tokens": 136 + }, + { + "name": "TestOutboundWorker_TestingModePolicyStillDelivers", + "kind": "fn", + "startLine": 177, + "endLine": 190, + "tokens": 148 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/outbound_worker_test.go": { + "description": "fakeOutboundRepo (18 fields); methods: Enqueue, FetchNextReady, CountRecipientsSince, UpdateJob", + "tokens": 744, + "hash": "5af9492821e37c4c", + "size": 2601, + "mtimeMs": 1785635482522.9756, + "updatedAt": "2026-10-06T11:06:20.866Z", + "source": "scan", + "symbols": [ + { + "name": "fakeOutboundRepo", + "kind": "class", + "startLine": 14, + "endLine": 16, + "tokens": 18 + }, + { + "name": "Enqueue", + "kind": "method", + "startLine": 18, + "endLine": 21, + "tokens": 38 + }, + { + "name": "FetchNextReady", + "kind": "method", + "startLine": 23, + "endLine": 31, + "tokens": 93 + }, + { + "name": "CountRecipientsSince", + "kind": "method", + "startLine": 33, + "endLine": 35, + "tokens": 36 + }, + { + "name": "UpdateJob", + "kind": "method", + "startLine": 37, + "endLine": 45, + "tokens": 54 + }, + { + "name": "fakeMXResolver", + "kind": "class", + "startLine": 47, + "endLine": 49, + "tokens": 14 + }, + { + "name": "LookupMX", + "kind": "method", + "startLine": 51, + "endLine": 53, + "tokens": 30 + }, + { + "name": "fakeBlobReader", + "kind": "class", + "startLine": 55, + "endLine": 57, + "tokens": 14 + }, + { + "name": "ReadByID", + "kind": "method", + "startLine": 59, + "endLine": 61, + "tokens": 31 + }, + { + "name": "TestOutboundWorker_DeferralAndPermanentFailure", + "kind": "fn", + "startLine": 63, + "endLine": 96, + "tokens": 374 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/outbound_worker.go": { + "description": "OutboundWorkerUseCase (72 fields); methods: SetRelay, SetPolicyResolver, ProcessBatch", + "tokens": 4121, + "hash": "fac5ed14ccd2df81", + "size": 14423, + "mtimeMs": 1785712492462.385, + "updatedAt": "2026-10-06T11:06:20.866Z", + "source": "scan", + "symbols": [ + { + "name": "OutboundWorkerUseCase", + "kind": "class", + "startLine": 27, + "endLine": 36, + "tokens": 90 + }, + { + "name": "SetRelay", + "kind": "method", + "startLine": 41, + "endLine": 43, + "tokens": 23 + }, + { + "name": "SetPolicyResolver", + "kind": "method", + "startLine": 49, + "endLine": 51, + "tokens": 33 + }, + { + "name": "NewOutboundWorkerUseCase", + "kind": "fn", + "startLine": 53, + "endLine": 72, + "tokens": 148 + }, + { + "name": "ProcessBatch", + "kind": "method", + "startLine": 74, + "endLine": 90, + "tokens": 114 + }, + { + "name": "processSingleJob", + "kind": "method", + "startLine": 92, + "endLine": 184, + "tokens": 867 + }, + { + "name": "finishFailed", + "kind": "method", + "startLine": 187, + "endLine": 212, + "tokens": 261 + }, + { + "name": "smtpReplyCode", + "kind": "fn", + "startLine": 217, + "endLine": 223, + "tokens": 39 + }, + { + "name": "handleFailure", + "kind": "method", + "startLine": 225, + "endLine": 248, + "tokens": 215 + }, + { + "name": "sendDsn", + "kind": "method", + "startLine": 250, + "endLine": 286, + "tokens": 343 + }, + { + "name": "resolvePolicy", + "kind": "method", + "startLine": 290, + "endLine": 295, + "tokens": 79 + }, + { + "name": "tlsConfigFor", + "kind": "fn", + "startLine": 304, + "endLine": 341, + "tokens": 370 + }, + { + "name": "deliverToMX", + "kind": "method", + "startLine": 343, + "endLine": 404, + "tokens": 561 + }, + { + "name": "deliverLocally", + "kind": "method", + "startLine": 416, + "endLine": 445, + "tokens": 256 + } + ], + "symbolSource": "ts", + "skeleton": "type OutboundWorkerUseCase struct {\nfunc (w *OutboundWorkerUseCase) SetRelay(relay RelayConfig) {\nfunc (w *OutboundWorkerUseCase) SetPolicyResolver(resolver port.TLSPolicyResolver) {\nfunc NewOutboundWorkerUseCase(\nfunc (w *OutboundWorkerUseCase) ProcessBatch(ctx context.Context, workerID string, limit int) (int, error) {\nfunc (w *OutboundWorkerUseCase) processSingleJob(ctx context.Context, job *entity.OutboundJob) {\nfunc (w *OutboundWorkerUseCase) finishFailed(ctx context.Context, job *entity.OutboundJob, lastErr error) {\nfunc smtpReplyCode(err error) int {\nfunc (w *OutboundWorkerUseCase) handleFailure(ctx context.Context, job *entity.OutboundJob, errStr string, isPermanent bool) {\nfunc (w *OutboundWorkerUseCase) sendDsn(ctx context.Context, action valueobject.DsnAction, job *entity.OutboundJob, reason string) {\nfunc (w *OutboundWorkerUseCase) resolvePolicy(ctx context.Context, destinationDomain, mxHost string) (entity.TLSPolicy, error) {\nfunc tlsConfigFor(mxHost string, policy entity.TLSPolicy) *tls.Config {\nfunc (w *OutboundWorkerUseCase) deliverToMX(ctx context.Context, mxHost string, from string, to string, payload []byte, policy entity.TLSPolicy) error {\nfunc (w *OutboundWorkerUseCase) deliverLocally(", + "imports": [ + "services/protocols/internal/infrastructure/tls/acme_cert_watcher_test.go", + "services/protocols/internal/presentation/smtp/backend_test.go", + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/domain/entity/dkim_key_pair_test.go", + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/outbox_relay.go": { + "description": "OutboxRelay (16 fields); methods: Run, PublishBatch", + "tokens": 551, + "hash": "07e3450f932db268", + "size": 1927, + "mtimeMs": 1785689161518.2874, + "updatedAt": "2026-10-06T11:06:20.866Z", + "source": "scan", + "symbols": [ + { + "name": "OutboxRelay", + "kind": "class", + "startLine": 19, + "endLine": 24, + "tokens": 38 + }, + { + "name": "NewOutboxRelay", + "kind": "fn", + "startLine": 26, + "endLine": 28, + "tokens": 54 + }, + { + "name": "Run", + "kind": "method", + "startLine": 31, + "endLine": 45, + "tokens": 80 + }, + { + "name": "PublishBatch", + "kind": "method", + "startLine": 48, + "endLine": 70, + "tokens": 186 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/pop3_session_test.go": { + "description": "fakeMessageQueryRepository (12 fields); methods: ListMessages", + "tokens": 706, + "hash": "334558608e8074a1", + "size": 2468, + "mtimeMs": 1785635482522.9756, + "updatedAt": "2026-10-06T11:06:20.866Z", + "source": "scan", + "symbols": [ + { + "name": "TestPop3SessionUseCase_Login_SucceedsForCorrectPassword", + "kind": "fn", + "startLine": 12, + "endLine": 26, + "tokens": 157 + }, + { + "name": "TestPop3SessionUseCase_Login_FailsForWrongPassword", + "kind": "fn", + "startLine": 28, + "endLine": 38, + "tokens": 128 + }, + { + "name": "fakeMessageQueryRepository", + "kind": "class", + "startLine": 40, + "endLine": 42, + "tokens": 21 + }, + { + "name": "ListMessages", + "kind": "method", + "startLine": 44, + "endLine": 46, + "tokens": 39 + }, + { + "name": "TestPop3SessionUseCase_GetInbox_ReturnsFolderAndMessages", + "kind": "fn", + "startLine": 48, + "endLine": 64, + "tokens": 191 + }, + { + "name": "TestPop3SessionUseCase_ExpungeMessages_DelegatesToRepo", + "kind": "fn", + "startLine": 66, + "endLine": 76, + "tokens": 129 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/pop3_session.go": { + "description": "Pop3SessionUseCase (31 fields); methods: Login, GetInbox, ReadBlob, ExpungeMessages", + "tokens": 690, + "hash": "c9f8fd5a48cb11e8", + "size": 2413, + "mtimeMs": 1785635482522.9756, + "updatedAt": "2026-10-06T11:06:20.866Z", + "source": "scan", + "symbols": [ + { + "name": "Pop3SessionUseCase", + "kind": "class", + "startLine": 13, + "endLine": 19, + "tokens": 62 + }, + { + "name": "NewPop3SessionUseCase", + "kind": "fn", + "startLine": 21, + "endLine": 35, + "tokens": 116 + }, + { + "name": "Login", + "kind": "method", + "startLine": 37, + "endLine": 53, + "tokens": 147 + }, + { + "name": "GetInbox", + "kind": "method", + "startLine": 55, + "endLine": 68, + "tokens": 160 + }, + { + "name": "ReadBlob", + "kind": "method", + "startLine": 70, + "endLine": 76, + "tokens": 72 + }, + { + "name": "ExpungeMessages", + "kind": "method", + "startLine": 78, + "endLine": 86, + "tokens": 86 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/preflight_test.go": { + "description": "TestPreflight_HealthyDeploymentDoesNotBlock, TestPreflight_BlockedPort25IsCriticalAndSuggestsSmarthost, TestPreflight_SmarthostSkipsPort25Check, Te...", + "tokens": 2796, + "hash": "b1d4b2e8c4674156", + "size": 9785, + "mtimeMs": 1785635482522.9756, + "updatedAt": "2026-10-06T11:06:20.866Z", + "source": "scan", + "symbols": [ + { + "name": "healthyDeps", + "kind": "fn", + "startLine": 13, + "endLine": 26, + "tokens": 248 + }, + { + "name": "healthyInput", + "kind": "fn", + "startLine": 28, + "endLine": 35, + "tokens": 58 + }, + { + "name": "findCheck", + "kind": "fn", + "startLine": 37, + "endLine": 46, + "tokens": 86 + }, + { + "name": "TestPreflight_HealthyDeploymentDoesNotBlock", + "kind": "fn", + "startLine": 48, + "endLine": 54, + "tokens": 74 + }, + { + "name": "TestPreflight_BlockedPort25IsCriticalAndSuggestsSmarthost", + "kind": "fn", + "startLine": 57, + "endLine": 73, + "tokens": 191 + }, + { + "name": "TestPreflight_SmarthostSkipsPort25Check", + "kind": "fn", + "startLine": 77, + "endLine": 92, + "tokens": 153 + }, + { + "name": "TestPreflight_MissingPtrIsCriticalAndNamesTheProvider", + "kind": "fn", + "startLine": 96, + "endLine": 109, + "tokens": 170 + }, + { + "name": "TestPreflight_ForwardConfirmedReverseDnsMismatchIsCritical", + "kind": "fn", + "startLine": 111, + "endLine": 120, + "tokens": 133 + }, + { + "name": "TestPreflight_ListedIpIsCriticalAndNamesTheList", + "kind": "fn", + "startLine": 122, + "endLine": 140, + "tokens": 203 + }, + { + "name": "TestPreflight_MissingCertificateExplainsTheTraefikRouter", + "kind": "fn", + "startLine": 144, + "endLine": 157, + "tokens": 161 + }, + { + "name": "TestPreflight_DaneUnderTraefikModeIsRefused", + "kind": "fn", + "startLine": 161, + "endLine": 175, + "tokens": 141 + }, + { + "name": "TestPreflight_DaneUnderAcmeModePasses", + "kind": "fn", + "startLine": 177, + "endLine": 187, + "tokens": 104 + }, + { + "name": "TestPreflight_ClockDriftIsHighNotCritical", + "kind": "fn", + "startLine": 191, + "endLine": 206, + "tokens": 184 + }, + { + "name": "TestPreflight_LowDiskIsMediumSeverity", + "kind": "fn", + "startLine": 208, + "endLine": 218, + "tokens": 120 + }, + { + "name": "TestPreflight_IPv6WithoutPtrFailsButDisabledIPv6Passes", + "kind": "fn", + "startLine": 222, + "endLine": 243, + "tokens": 234 + }, + { + "name": "TestPreflight_MissingProbesAreSkippedNotPassed", + "kind": "fn", + "startLine": 246, + "endLine": 257, + "tokens": 129 + }, + { + "name": "TestReverseIPv4", + "kind": "fn", + "startLine": 259, + "endLine": 266, + "tokens": 79 + } + ], + "symbolSource": "ts", + "skeleton": "func healthyDeps() PreflightDeps {\nfunc healthyInput() PreflightInput {\nfunc findCheck(t *testing.T, report PreflightReport, substring string) CheckResult {\nfunc TestPreflight_HealthyDeploymentDoesNotBlock(t *testing.T) {\nfunc TestPreflight_BlockedPort25IsCriticalAndSuggestsSmarthost(t *testing.T) {\nfunc TestPreflight_SmarthostSkipsPort25Check(t *testing.T) {\nfunc TestPreflight_MissingPtrIsCriticalAndNamesTheProvider(t *testing.T) {\nfunc TestPreflight_ForwardConfirmedReverseDnsMismatchIsCritical(t *testing.T) {\nfunc TestPreflight_ListedIpIsCriticalAndNamesTheList(t *testing.T) {\nfunc TestPreflight_MissingCertificateExplainsTheTraefikRouter(t *testing.T) {\nfunc TestPreflight_DaneUnderTraefikModeIsRefused(t *testing.T) {\nfunc TestPreflight_DaneUnderAcmeModePasses(t *testing.T) {\nfunc TestPreflight_ClockDriftIsHighNotCritical(t *testing.T) {\nfunc TestPreflight_LowDiskIsMediumSeverity(t *testing.T) {\nfunc TestPreflight_IPv6WithoutPtrFailsButDisabledIPv6Passes(t *testing.T) {\nfunc TestPreflight_MissingProbesAreSkippedNotPassed(t *testing.T) {\nfunc TestReverseIPv4(t *testing.T) {", + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/preflight.go": { + "description": "CheckResult (112 fields); methods: Blocking, String, Execute", + "tokens": 3892, + "hash": "d9b80026554a1c73", + "size": 13621, + "mtimeMs": 1785635482522.9756, + "updatedAt": "2026-10-06T11:06:20.867Z", + "source": "scan", + "symbols": [ + { + "name": "Severity", + "kind": "class", + "startLine": 13, + "endLine": 13, + "tokens": 6 + }, + { + "name": "CheckStatus", + "kind": "class", + "startLine": 22, + "endLine": 22, + "tokens": 7 + }, + { + "name": "CheckResult", + "kind": "class", + "startLine": 31, + "endLine": 40, + "tokens": 94 + }, + { + "name": "PreflightReport", + "kind": "class", + "startLine": 43, + "endLine": 45, + "tokens": 16 + }, + { + "name": "Blocking", + "kind": "method", + "startLine": 49, + "endLine": 56, + "tokens": 55 + }, + { + "name": "String", + "kind": "method", + "startLine": 58, + "endLine": 71, + "tokens": 127 + }, + { + "name": "PreflightDeps", + "kind": "class", + "startLine": 75, + "endLine": 94, + "tokens": 308 + }, + { + "name": "PreflightInput", + "kind": "class", + "startLine": 97, + "endLine": 105, + "tokens": 56 + }, + { + "name": "PreflightUseCase", + "kind": "class", + "startLine": 108, + "endLine": 110, + "tokens": 15 + }, + { + "name": "NewPreflightUseCase", + "kind": "fn", + "startLine": 112, + "endLine": 114, + "tokens": 30 + }, + { + "name": "Execute", + "kind": "method", + "startLine": 128, + "endLine": 144, + "tokens": 169 + }, + { + "name": "checkPort25", + "kind": "method", + "startLine": 146, + "endLine": 167, + "tokens": 244 + }, + { + "name": "checkReverseDNS", + "kind": "method", + "startLine": 169, + "endLine": 196, + "tokens": 318 + }, + { + "name": "checkForwardConfirmedReverseDNS", + "kind": "method", + "startLine": 198, + "endLine": 225, + "tokens": 303 + }, + { + "name": "checkBlocklists", + "kind": "method", + "startLine": 227, + "endLine": 261, + "tokens": 317 + }, + { + "name": "checkCertificate", + "kind": "method", + "startLine": 263, + "endLine": 278, + "tokens": 184 + }, + { + "name": "checkDaneRequiresAcmeMode", + "kind": "method", + "startLine": 280, + "endLine": 298, + "tokens": 216 + }, + { + "name": "checkMtaStsEndpoint", + "kind": "method", + "startLine": 300, + "endLine": 314, + "tokens": 209 + }, + { + "name": "checkClockDrift", + "kind": "method", + "startLine": 316, + "endLine": 341, + "tokens": 239 + }, + { + "name": "checkDiskSpace", + "kind": "method", + "startLine": 343, + "endLine": 362, + "tokens": 182 + }, + { + "name": "checkIPv6Coherence", + "kind": "method", + "startLine": 364, + "endLine": 383, + "tokens": 211 + }, + { + "name": "passed", + "kind": "fn", + "startLine": 385, + "endLine": 387, + "tokens": 47 + }, + { + "name": "skipped", + "kind": "fn", + "startLine": 389, + "endLine": 394, + "tokens": 56 + }, + { + "name": "reverseIPv4", + "kind": "fn", + "startLine": 398, + "endLine": 404, + "tokens": 49 + } + ], + "symbolSource": "ts", + "skeleton": "type Severity string\ntype CheckStatus string\ntype CheckResult struct {\ntype PreflightReport struct {\nfunc (r PreflightReport) Blocking() bool {\nfunc (r PreflightReport) String() string {\ntype PreflightDeps struct {\ntype PreflightInput struct {\ntype PreflightUseCase struct {\nfunc NewPreflightUseCase(deps PreflightDeps) *PreflightUseCase {\nfunc (uc *PreflightUseCase) Execute(ctx context.Context, input PreflightInput) PreflightReport {\nfunc (uc *PreflightUseCase) checkPort25(ctx context.Context, input PreflightInput) CheckResult {\nfunc (uc *PreflightUseCase) checkReverseDNS(ctx context.Context, input PreflightInput) CheckResult {\nfunc (uc *PreflightUseCase) checkForwardConfirmedReverseDNS(ctx context.Context, input PreflightInput) CheckResult {\nfunc (uc *PreflightUseCase) checkBlocklists(ctx context.Context, input PreflightInput) CheckResult {\nfunc (uc *PreflightUseCase) checkCertificate(input PreflightInput) CheckResult {\nfunc (uc *PreflightUseCase) checkDaneRequiresAcmeMode(input PreflightInput) CheckResult {\nfunc (uc *PreflightUseCase) checkMtaStsEndpoint(ctx context.Context, input PreflightInput) CheckResult {\nfunc (uc *PreflightUseCase) checkClockDrift(ctx context.Context) CheckResult {\nfunc (uc *PreflightUseCase) checkDiskSpace() CheckResult {\nfunc (uc *PreflightUseCase) checkIPv6Coherence(ctx context.Context, input PreflightInput) CheckResult {", + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/process_inbound_email_test.go": { + "description": "fakeMailboxRepository (36 fields); methods: FindActiveByAddress, ResolveDeliveryTargets, FindActiveByAddress, ResolveDeliveryTargets", + "tokens": 2332, + "hash": "2bbdeba5c3f4eb22", + "size": 8162, + "mtimeMs": 1785712539733.18, + "updatedAt": "2026-10-06T11:06:20.867Z", + "source": "scan", + "symbols": [ + { + "name": "fakeMailboxRepository", + "kind": "class", + "startLine": 14, + "endLine": 16, + "tokens": 23 + }, + { + "name": "FindActiveByAddress", + "kind": "method", + "startLine": 18, + "endLine": 24, + "tokens": 58 + }, + { + "name": "ResolveDeliveryTargets", + "kind": "method", + "startLine": 26, + "endLine": 32, + "tokens": 70 + }, + { + "name": "TestUseCase_ResolveRecipient_ReturnsRecordForActiveMailbox", + "kind": "fn", + "startLine": 34, + "endLine": 48, + "tokens": 173 + }, + { + "name": "TestUseCase_ResolveRecipient_ReturnsErrRecipientNotFoundForUnknownAddress", + "kind": "fn", + "startLine": 50, + "endLine": 58, + "tokens": 119 + }, + { + "name": "TestUseCase_ResolveRecipient_ReturnsErrMailboxQuotaExceededWhenFull", + "kind": "fn", + "startLine": 60, + "endLine": 70, + "tokens": 141 + }, + { + "name": "TestUseCase_ResolveRecipient_ReturnsErrMailboxQuotaExceededWhenAnyFanOutTargetIsFull", + "kind": "fn", + "startLine": 72, + "endLine": 83, + "tokens": 162 + }, + { + "name": "fakeAliasFanOutRepository", + "kind": "class", + "startLine": 92, + "endLine": 95, + "tokens": 28 + }, + { + "name": "FindActiveByAddress", + "kind": "method", + "startLine": 97, + "endLine": 99, + "tokens": 39 + }, + { + "name": "ResolveDeliveryTargets", + "kind": "method", + "startLine": 101, + "endLine": 106, + "tokens": 71 + }, + { + "name": "stubBlobStorage", + "kind": "class", + "startLine": 108, + "endLine": 111, + "tokens": 18 + }, + { + "name": "Store", + "kind": "method", + "startLine": 113, + "endLine": 116, + "tokens": 41 + }, + { + "name": "recordingMessageRepository", + "kind": "class", + "startLine": 121, + "endLine": 127, + "tokens": 74 + }, + { + "name": "PersistAll", + "kind": "method", + "startLine": 129, + "endLine": 143, + "tokens": 154 + }, + { + "name": "TestUseCase_Handle_StoresBlobOnceAndPersistsOncePerRecipient", + "kind": "fn", + "startLine": 145, + "endLine": 184, + "tokens": 477 + }, + { + "name": "TestUseCase_Handle_PropagatesBlobStorageError", + "kind": "fn", + "startLine": 186, + "endLine": 199, + "tokens": 162 + }, + { + "name": "TestUseCase_Handle_DoesNotPartiallyDeliverOnFanOut", + "kind": "fn", + "startLine": 204, + "endLine": 227, + "tokens": 266 + } + ], + "symbolSource": "ts", + "skeleton": "type fakeMailboxRepository struct {\nfunc (f *fakeMailboxRepository) FindActiveByAddress(_ context.Context, address string) (*port.MailboxRecord, error) {\nfunc (f *fakeMailboxRepository) ResolveDeliveryTargets(_ context.Context, address string) ([]port.MailboxRecord, error) {\nfunc TestUseCase_ResolveRecipient_ReturnsRecordForActiveMailbox(t *testing.T) {\nfunc TestUseCase_ResolveRecipient_ReturnsErrRecipientNotFoundForUnknownAddress(t *testing.T) {\nfunc TestUseCase_ResolveRecipient_ReturnsErrMailboxQuotaExceededWhenFull(t *testing.T) {\nfunc TestUseCase_ResolveRecipient_ReturnsErrMailboxQuotaExceededWhenAnyFanOutTargetIsFull(t *testing.T) {\ntype fakeAliasFanOutRepository struct {\nfunc (f *fakeAliasFanOutRepository) FindActiveByAddress(_ context.Context, _ string) (*port.MailboxRecord, error) {\nfunc (f *fakeAliasFanOutRepository) ResolveDeliveryTargets(_ context.Context, address string) ([]port.MailboxRecord, error) {\ntype stubBlobStorage struct {\nfunc (s *stubBlobStorage) Store(_ context.Context, r io.Reader) (port.BlobRef, error) {\ntype recordingMessageRepository struct {\nfunc (r *recordingMessageRepository) PersistAll(_ context.Context, inputs []port.PersistInboundMessageInput) ([]int64, error) {\nfunc TestUseCase_Handle_StoresBlobOnceAndPersistsOncePerRecipient(t *testing.T) {\nfunc TestUseCase_Handle_PropagatesBlobStorageError(t *testing.T) {", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/process_inbound_email.go": { + "description": "contains the application's use cases (Clean Architecture layer 2).", + "tokens": 4084, + "hash": "4f96751cf4a0b935", + "size": 14292, + "mtimeMs": 1787422439293.8254, + "updatedAt": "2026-10-06T11:06:20.867Z", + "source": "scan", + "symbols": [ + { + "name": "ProcessInboundEmailInput", + "kind": "class", + "startLine": 33, + "endLine": 53, + "tokens": 283 + }, + { + "name": "ProcessInboundEmailUseCase", + "kind": "class", + "startLine": 59, + "endLine": 74, + "tokens": 163 + }, + { + "name": "NewProcessInboundEmailUseCase", + "kind": "fn", + "startLine": 76, + "endLine": 78, + "tokens": 74 + }, + { + "name": "SetRules", + "kind": "method", + "startLine": 82, + "endLine": 84, + "tokens": 27 + }, + { + "name": "SetReportIngestor", + "kind": "method", + "startLine": 89, + "endLine": 91, + "tokens": 38 + }, + { + "name": "SetScanner", + "kind": "method", + "startLine": 93, + "endLine": 95, + "tokens": 30 + }, + { + "name": "SetAuthenticator", + "kind": "method", + "startLine": 97, + "endLine": 99, + "tokens": 38 + }, + { + "name": "SetArcSealer", + "kind": "method", + "startLine": 103, + "endLine": 105, + "tokens": 29 + }, + { + "name": "SetTrackerManager", + "kind": "method", + "startLine": 107, + "endLine": 110, + "tokens": 48 + }, + { + "name": "prependHeaders", + "kind": "fn", + "startLine": 116, + "endLine": 138, + "tokens": 165 + }, + { + "name": "ResolveRecipient", + "kind": "method", + "startLine": 143, + "endLine": 157, + "tokens": 121 + }, + { + "name": "Handle", + "kind": "method", + "startLine": 164, + "endLine": 377, + "tokens": 2261 + } + ], + "symbolSource": "ts", + "skeleton": "type ProcessInboundEmailInput struct {\ntype ProcessInboundEmailUseCase struct {\nfunc NewProcessInboundEmailUseCase(mailboxes port.MailboxRepository, blobs port.BlobStorage, messages port.InboundMessageRepository) *ProcessInboundEmailUseCase {\nfunc (uc *ProcessInboundEmailUseCase) SetRules(rules *ApplyRulesUseCase) {\nfunc (uc *ProcessInboundEmailUseCase) SetReportIngestor(ingestor *IngestDeliveredReportsUseCase) {\nfunc (uc *ProcessInboundEmailUseCase) SetScanner(scanner port.ContentScanner) {\nfunc (uc *ProcessInboundEmailUseCase) SetAuthenticator(authenticator port.MailAuthenticator) {\nfunc (uc *ProcessInboundEmailUseCase) SetArcSealer(sealer port.ArcSealer) {\nfunc (uc *ProcessInboundEmailUseCase) SetTrackerManager(tm *MailboxTrackerManager, folders port.ImapFolderRepository) {\nfunc prependHeaders(payload []byte, headers map[string]string) []byte {\nfunc (uc *ProcessInboundEmailUseCase) ResolveRecipient(ctx context.Context, address string) ([]port.MailboxRecord, error) {\nfunc (uc *ProcessInboundEmailUseCase) Handle(ctx context.Context, input ProcessInboundEmailInput) error {", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/process_outbound_email_test.go": { + "description": "fakeAuthRepo (31 fields); methods: FindByAddress, CountRecipientsSince, Sign", + "tokens": 1768, + "hash": "f36202b378f38bda", + "size": 6187, + "mtimeMs": 1785635482522.9756, + "updatedAt": "2026-10-06T11:06:20.867Z", + "source": "scan", + "symbols": [ + { + "name": "fakeAuthRepo", + "kind": "class", + "startLine": 18, + "endLine": 20, + "tokens": 16 + }, + { + "name": "FindByAddress", + "kind": "method", + "startLine": 22, + "endLine": 24, + "tokens": 34 + }, + { + "name": "countingOutboundRepo", + "kind": "class", + "startLine": 26, + "endLine": 29, + "tokens": 21 + }, + { + "name": "CountRecipientsSince", + "kind": "method", + "startLine": 31, + "endLine": 33, + "tokens": 40 + }, + { + "name": "recordingSigner", + "kind": "class", + "startLine": 35, + "endLine": 37, + "tokens": 16 + }, + { + "name": "Sign", + "kind": "method", + "startLine": 39, + "endLine": 42, + "tokens": 64 + }, + { + "name": "submissionInput", + "kind": "fn", + "startLine": 44, + "endLine": 53, + "tokens": 112 + }, + { + "name": "TestSubmit_SignsAndEnqueuesOneJobPerRecipient", + "kind": "fn", + "startLine": 57, + "endLine": 95, + "tokens": 441 + }, + { + "name": "TestSubmit_RefusesWhenHourlyRecipientLimitWouldBeExceeded", + "kind": "fn", + "startLine": 99, + "endLine": 111, + "tokens": 149 + }, + { + "name": "TestSubmit_AllowsSubmissionExactlyAtTheLimit", + "kind": "fn", + "startLine": 113, + "endLine": 123, + "tokens": 130 + }, + { + "name": "TestAuthenticate_AcceptsCorrectPasswordAndRejectsWrongOne", + "kind": "fn", + "startLine": 125, + "endLine": 142, + "tokens": 222 + }, + { + "name": "TestAuthenticate_UnknownMailboxYieldsTheSameError", + "kind": "fn", + "startLine": 146, + "endLine": 152, + "tokens": 98 + }, + { + "name": "TestAuthorizeSender_RejectsForeignEnvelopeSender", + "kind": "fn", + "startLine": 156, + "endLine": 171, + "tokens": 214 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/process_outbound_email.go": { + "description": "ProcessOutboundEmailInput (50 fields); methods: Authenticate, Submit", + "tokens": 1739, + "hash": "8b74c29b04ae7d01", + "size": 6085, + "mtimeMs": 1785635482522.9756, + "updatedAt": "2026-10-06T11:06:20.867Z", + "source": "scan", + "symbols": [ + { + "name": "ProcessOutboundEmailInput", + "kind": "class", + "startLine": 42, + "endLine": 51, + "tokens": 76 + }, + { + "name": "ProcessOutboundEmailUseCase", + "kind": "class", + "startLine": 55, + "endLine": 60, + "tokens": 46 + }, + { + "name": "NewProcessOutboundEmailUseCase", + "kind": "fn", + "startLine": 62, + "endLine": 69, + "tokens": 81 + }, + { + "name": "Authenticate", + "kind": "method", + "startLine": 73, + "endLine": 90, + "tokens": 178 + }, + { + "name": "Submit", + "kind": "method", + "startLine": 99, + "endLine": 147, + "tokens": 382 + }, + { + "name": "checkSendLimit", + "kind": "method", + "startLine": 152, + "endLine": 167, + "tokens": 171 + }, + { + "name": "AuthorizeSender", + "kind": "fn", + "startLine": 171, + "endLine": 179, + "tokens": 74 + }, + { + "name": "recipientDomain", + "kind": "fn", + "startLine": 181, + "endLine": 186, + "tokens": 52 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/provision_dkim_keys_test.go": { + "description": "fakeDkimStore (26 fields); methods: FindPublicKey, Insert, FindPublicKey, Insert", + "tokens": 1089, + "hash": "f2d40edf1c36c807", + "size": 3811, + "mtimeMs": 1785635482522.9756, + "updatedAt": "2026-10-06T11:06:20.867Z", + "source": "scan", + "symbols": [ + { + "name": "fakeDkimStore", + "kind": "class", + "startLine": 9, + "endLine": 13, + "tokens": 28 + }, + { + "name": "newFakeDkimStore", + "kind": "fn", + "startLine": 15, + "endLine": 17, + "tokens": 28 + }, + { + "name": "FindPublicKey", + "kind": "method", + "startLine": 19, + "endLine": 21, + "tokens": 39 + }, + { + "name": "Insert", + "kind": "method", + "startLine": 23, + "endLine": 30, + "tokens": 80 + }, + { + "name": "countingGenerator", + "kind": "fn", + "startLine": 32, + "endLine": 37, + "tokens": 61 + }, + { + "name": "TestProvisionDkimKeys_CreatesBothAlgorithms", + "kind": "fn", + "startLine": 41, + "endLine": 60, + "tokens": 179 + }, + { + "name": "TestProvisionDkimKeys_IsIdempotent", + "kind": "fn", + "startLine": 64, + "endLine": 89, + "tokens": 231 + }, + { + "name": "TestProvisionDkimKeys_ReturnsStoredKeyAfterLostRace", + "kind": "fn", + "startLine": 93, + "endLine": 106, + "tokens": 142 + }, + { + "name": "raceStore", + "kind": "class", + "startLine": 110, + "endLine": 113, + "tokens": 21 + }, + { + "name": "FindPublicKey", + "kind": "method", + "startLine": 115, + "endLine": 124, + "tokens": 74 + }, + { + "name": "Insert", + "kind": "method", + "startLine": 126, + "endLine": 128, + "tokens": 39 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/provision_dkim_keys.go": { + "description": "Interface: DkimKeyProvisioner (5 methods)", + "tokens": 1121, + "hash": "9afd6b429c71ad0d", + "size": 3923, + "mtimeMs": 1785759222668.893, + "updatedAt": "2026-10-06T11:06:20.867Z", + "source": "scan", + "symbols": [ + { + "name": "DkimKeyProvisioner", + "kind": "class", + "startLine": 12, + "endLine": 15, + "tokens": 67 + }, + { + "name": "DkimKeyGenerator", + "kind": "class", + "startLine": 18, + "endLine": 18, + "tokens": 30 + }, + { + "name": "ProvisionDkimKeysOutput", + "kind": "class", + "startLine": 32, + "endLine": 36, + "tokens": 34 + }, + { + "name": "ProvisionDkimKeysUseCase", + "kind": "class", + "startLine": 42, + "endLine": 45, + "tokens": 28 + }, + { + "name": "NewProvisionDkimKeysUseCase", + "kind": "fn", + "startLine": 47, + "endLine": 49, + "tokens": 52 + }, + { + "name": "Execute", + "kind": "method", + "startLine": 51, + "endLine": 71, + "tokens": 164 + }, + { + "name": "ensure", + "kind": "method", + "startLine": 73, + "endLine": 112, + "tokens": 481 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/relay_test.go": { + "description": "fakeRelay (59 fields)", + "tokens": 3001, + "hash": "daed7337422d6569", + "size": 10501, + "mtimeMs": 1785713018572.3328, + "updatedAt": "2026-10-06T11:06:20.868Z", + "source": "scan", + "symbols": [ + { + "name": "fakeRelay", + "kind": "class", + "startLine": 24, + "endLine": 30, + "tokens": 41 + }, + { + "name": "startFakeRelay", + "kind": "fn", + "startLine": 32, + "endLine": 62, + "tokens": 158 + }, + { + "name": "serve", + "kind": "method", + "startLine": 64, + "endLine": 133, + "tokens": 508 + }, + { + "name": "relayWorker", + "kind": "fn", + "startLine": 135, + "endLine": 151, + "tokens": 160 + }, + { + "name": "TestOutboundWorker_DeliversThroughRelayInsteadOfMX", + "kind": "fn", + "startLine": 155, + "endLine": 196, + "tokens": 372 + }, + { + "name": "TestOutboundWorker_RefusesRelayCredentialsWithoutStartTLS", + "kind": "fn", + "startLine": 200, + "endLine": 220, + "tokens": 199 + }, + { + "name": "TestOutboundWorker_UnreachableRelayDefers", + "kind": "fn", + "startLine": 223, + "endLine": 233, + "tokens": 111 + }, + { + "name": "TestRelayConfig_AddressDefaultsToSubmissionPort", + "kind": "fn", + "startLine": 235, + "endLine": 242, + "tokens": 115 + }, + { + "name": "TestRelayConfig_SpfInclude", + "kind": "fn", + "startLine": 246, + "endLine": 259, + "tokens": 119 + }, + { + "name": "atoiOrZero", + "kind": "fn", + "startLine": 261, + "endLine": 270, + "tokens": 46 + }, + { + "name": "relayTestCertificate", + "kind": "fn", + "startLine": 275, + "endLine": 306, + "tokens": 312 + }, + { + "name": "TestRelayConfig_SpfIncludeUsesTheConfiguredMechanism", + "kind": "fn", + "startLine": 319, + "endLine": 329, + "tokens": 152 + }, + { + "name": "TestRelayConfig_SpfMechanismAcceptsBareDomainsAndOtherTerms", + "kind": "fn", + "startLine": 331, + "endLine": 344, + "tokens": 154 + }, + { + "name": "TestRelayConfig_SpfIncludeEmptyWithoutRelay", + "kind": "fn", + "startLine": 348, + "endLine": 352, + "tokens": 65 + } + ], + "symbolSource": "ts", + "skeleton": "type fakeRelay struct {\nfunc startFakeRelay(t *testing.T, offerTLS bool) *fakeRelay {\nfunc (f *fakeRelay) serve(conn net.Conn) {\nfunc relayWorker(t *testing.T, relay RelayConfig) (*OutboundWorkerUseCase, *fakeOutboundRepo) {\nfunc TestOutboundWorker_DeliversThroughRelayInsteadOfMX(t *testing.T) {\nfunc TestOutboundWorker_RefusesRelayCredentialsWithoutStartTLS(t *testing.T) {\nfunc TestOutboundWorker_UnreachableRelayDefers(t *testing.T) {\nfunc TestRelayConfig_AddressDefaultsToSubmissionPort(t *testing.T) {\nfunc TestRelayConfig_SpfInclude(t *testing.T) {\nfunc atoiOrZero(s string) int {\nfunc relayTestCertificate(t *testing.T) tls.Certificate {\nfunc TestRelayConfig_SpfIncludeUsesTheConfiguredMechanism(t *testing.T) {\nfunc TestRelayConfig_SpfMechanismAcceptsBareDomainsAndOtherTerms(t *testing.T) {\nfunc TestRelayConfig_SpfIncludeEmptyWithoutRelay(t *testing.T) {", + "imports": [ + "services/protocols/internal/infrastructure/tls/acme_cert_watcher_test.go", + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/relay.go": { + "description": "RelayConfig (42 fields); methods: Configured, Address, SpfInclude", + "tokens": 1588, + "hash": "4f6c3e27e96af839", + "size": 5558, + "mtimeMs": 1785713002383.1174, + "updatedAt": "2026-10-06T11:06:20.868Z", + "source": "scan", + "symbols": [ + { + "name": "RelayConfig", + "kind": "class", + "startLine": 20, + "endLine": 34, + "tokens": 166 + }, + { + "name": "Configured", + "kind": "method", + "startLine": 37, + "endLine": 39, + "tokens": 18 + }, + { + "name": "Address", + "kind": "method", + "startLine": 42, + "endLine": 48, + "tokens": 41 + }, + { + "name": "SpfInclude", + "kind": "method", + "startLine": 66, + "endLine": 84, + "tokens": 166 + }, + { + "name": "authFor", + "kind": "method", + "startLine": 89, + "endLine": 94, + "tokens": 47 + }, + { + "name": "relayTLSConfig", + "kind": "fn", + "startLine": 100, + "endLine": 106, + "tokens": 48 + }, + { + "name": "deliverViaRelay", + "kind": "method", + "startLine": 109, + "endLine": 165, + "tokens": 491 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/infrastructure/tls/acme_cert_watcher_test.go", + "services/protocols/internal/presentation/smtp/backend_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/rollover_tlsa_test.go": { + "description": "TestRollover_StableWhenRenewalIsFarAway, TestRollover_StartsWhenRenewalApproaches, TestRollover_PublishesBothAssociationsWhileInFlight, TestRollove...", + "tokens": 2109, + "hash": "1668a48598e51010", + "size": 7381, + "mtimeMs": 1785635482522.9756, + "updatedAt": "2026-10-06T11:06:20.868Z", + "source": "scan", + "symbols": [ + { + "name": "rolloverAt", + "kind": "fn", + "startLine": 16, + "endLine": 20, + "tokens": 53 + }, + { + "name": "contains", + "kind": "fn", + "startLine": 22, + "endLine": 29, + "tokens": 38 + }, + { + "name": "TestRollover_StableWhenRenewalIsFarAway", + "kind": "fn", + "startLine": 35, + "endLine": 47, + "tokens": 131 + }, + { + "name": "TestRollover_StartsWhenRenewalApproaches", + "kind": "fn", + "startLine": 50, + "endLine": 59, + "tokens": 92 + }, + { + "name": "TestRollover_PublishesBothAssociationsWhileInFlight", + "kind": "fn", + "startLine": 64, + "endLine": 83, + "tokens": 209 + }, + { + "name": "TestRollover_WaitsOutPropagationBeforeRenewing", + "kind": "fn", + "startLine": 87, + "endLine": 107, + "tokens": 240 + }, + { + "name": "TestRollover_RetiresOldAssociationOnlyAfterPropagation", + "kind": "fn", + "startLine": 111, + "endLine": 136, + "tokens": 276 + }, + { + "name": "TestRollover_PublishesUnpublishedPendingAssociation", + "kind": "fn", + "startLine": 140, + "endLine": 151, + "tokens": 135 + }, + { + "name": "TestRollover_ShouldRenewNow", + "kind": "fn", + "startLine": 153, + "endLine": 165, + "tokens": 122 + }, + { + "name": "TestTlsaDigestForKey_MatchesTheIssuedCertificate", + "kind": "fn", + "startLine": 170, + "endLine": 211, + "tokens": 397 + }, + { + "name": "TestTlsaDigest_RejectsGarbage", + "kind": "fn", + "startLine": 213, + "endLine": 220, + "tokens": 86 + } + ], + "symbolSource": "ts", + "skeleton": "func rolloverAt(t *testing.T, now time.Time) *RolloverTlsaUseCase {\nfunc contains(values []string, want string) bool {\nfunc TestRollover_StableWhenRenewalIsFarAway(t *testing.T) {\nfunc TestRollover_StartsWhenRenewalApproaches(t *testing.T) {\nfunc TestRollover_PublishesBothAssociationsWhileInFlight(t *testing.T) {\nfunc TestRollover_WaitsOutPropagationBeforeRenewing(t *testing.T) {\nfunc TestRollover_RetiresOldAssociationOnlyAfterPropagation(t *testing.T) {\nfunc TestRollover_PublishesUnpublishedPendingAssociation(t *testing.T) {\nfunc TestRollover_ShouldRenewNow(t *testing.T) {\nfunc TestTlsaDigestForKey_MatchesTheIssuedCertificate(t *testing.T) {\nfunc TestTlsaDigest_RejectsGarbage(t *testing.T) {", + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/rollover_tlsa.go": { + "description": "RolloverConfig (50 fields); methods: WithClock, Decide, ShouldRenewNow", + "tokens": 2470, + "hash": "761bbb0a9280a22b", + "size": 8643, + "mtimeMs": 1785635482522.9756, + "updatedAt": "2026-10-06T11:06:20.868Z", + "source": "scan", + "symbols": [ + { + "name": "RolloverStage", + "kind": "class", + "startLine": 19, + "endLine": 19, + "tokens": 8 + }, + { + "name": "RolloverConfig", + "kind": "class", + "startLine": 40, + "endLine": 52, + "tokens": 182 + }, + { + "name": "DefaultRolloverConfig", + "kind": "fn", + "startLine": 55, + "endLine": 61, + "tokens": 54 + }, + { + "name": "RolloverState", + "kind": "class", + "startLine": 64, + "endLine": 77, + "tokens": 161 + }, + { + "name": "RolloverDecision", + "kind": "class", + "startLine": 80, + "endLine": 87, + "tokens": 75 + }, + { + "name": "RolloverTlsaUseCase", + "kind": "class", + "startLine": 93, + "endLine": 96, + "tokens": 24 + }, + { + "name": "NewRolloverTlsaUseCase", + "kind": "fn", + "startLine": 98, + "endLine": 103, + "tokens": 58 + }, + { + "name": "WithClock", + "kind": "method", + "startLine": 106, + "endLine": 109, + "tokens": 32 + }, + { + "name": "Decide", + "kind": "method", + "startLine": 111, + "endLine": 170, + "tokens": 664 + }, + { + "name": "ShouldRenewNow", + "kind": "method", + "startLine": 174, + "endLine": 179, + "tokens": 51 + }, + { + "name": "TlsaDigestForKey", + "kind": "fn", + "startLine": 185, + "endLine": 217, + "tokens": 302 + }, + { + "name": "TlsaDigestForCertificate", + "kind": "fn", + "startLine": 221, + "endLine": 233, + "tokens": 122 + }, + { + "name": "dedupe", + "kind": "fn", + "startLine": 235, + "endLine": 246, + "tokens": 67 + } + ], + "symbolSource": "ts", + "skeleton": "type RolloverStage string\ntype RolloverConfig struct {\nfunc DefaultRolloverConfig() RolloverConfig {\ntype RolloverState struct {\ntype RolloverDecision struct {\ntype RolloverTlsaUseCase struct {\nfunc NewRolloverTlsaUseCase(config RolloverConfig) *RolloverTlsaUseCase {\nfunc (uc *RolloverTlsaUseCase) WithClock(now func() time.Time) *RolloverTlsaUseCase {\nfunc (uc *RolloverTlsaUseCase) Decide(_ context.Context, state RolloverState) RolloverDecision {\nfunc (uc *RolloverTlsaUseCase) ShouldRenewNow(notAfter time.Time) bool {\nfunc TlsaDigestForKey(privateKeyPEM []byte) (string, error) {\nfunc TlsaDigestForCertificate(certificatePEM []byte) (string, error) {\nfunc dedupe(values ...string) []string {", + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/scanning_pipeline_test.go": { + "description": "mockScanner (9 fields); methods: Scan", + "tokens": 405, + "hash": "7a0e28167b001323", + "size": 1415, + "mtimeMs": 1785635482522.9756, + "updatedAt": "2026-10-06T11:06:20.868Z", + "source": "scan", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/scanning_pipeline.go": { + "description": "ScanningPipeline (13 fields); methods: Scan", + "tokens": 374, + "hash": "2031ba96a74df281", + "size": 1307, + "mtimeMs": 1785635482522.9756, + "updatedAt": "2026-10-06T11:06:20.868Z", + "source": "scan", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/smtp_rejection.go": { + "description": "SmtpRejection is a use case's decision to refuse a message, carrying the", + "tokens": 650, + "hash": "b28ffeb73871c8fe", + "size": 2274, + "mtimeMs": 1785668486701.793, + "updatedAt": "2026-10-06T11:06:20.869Z", + "source": "scan", + "symbols": [ + { + "name": "SmtpRejection", + "kind": "class", + "startLine": 14, + "endLine": 21, + "tokens": 78 + }, + { + "name": "Error", + "kind": "method", + "startLine": 23, + "endLine": 26, + "tokens": 46 + }, + { + "name": "IsPermanent", + "kind": "method", + "startLine": 29, + "endLine": 31, + "tokens": 24 + }, + { + "name": "rejectDmarc", + "kind": "fn", + "startLine": 35, + "endLine": 37, + "tokens": 43 + }, + { + "name": "rejectVirus", + "kind": "fn", + "startLine": 41, + "endLine": 43, + "tokens": 46 + }, + { + "name": "rejectSpam", + "kind": "fn", + "startLine": 46, + "endLine": 48, + "tokens": 40 + }, + { + "name": "deferGreylisted", + "kind": "fn", + "startLine": 53, + "endLine": 55, + "tokens": 44 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/sync_dns_conflict_test.go": { + "description": "stubDnsVerifier (32 fields); methods: VerifyRecord", + "tokens": 1548, + "hash": "14584b214bf590e6", + "size": 5416, + "mtimeMs": 1785635482522.9756, + "updatedAt": "2026-10-06T11:06:20.869Z", + "source": "scan", + "symbols": [ + { + "name": "conflictTestInput", + "kind": "fn", + "startLine": 11, + "endLine": 19, + "tokens": 67 + }, + { + "name": "TestSyncDnsRecords_ForeignApexTxtIsPreserved", + "kind": "fn", + "startLine": 25, + "endLine": 62, + "tokens": 301 + }, + { + "name": "TestSyncDnsRecords_ForeignARecordIsReportedAsConflict", + "kind": "fn", + "startLine": 67, + "endLine": 95, + "tokens": 271 + }, + { + "name": "TestSyncDnsRecords_OwnRecordDriftIsCorrected", + "kind": "fn", + "startLine": 98, + "endLine": 127, + "tokens": 272 + }, + { + "name": "stubDnsVerifier", + "kind": "class", + "startLine": 129, + "endLine": 131, + "tokens": 17 + }, + { + "name": "VerifyRecord", + "kind": "method", + "startLine": 133, + "endLine": 138, + "tokens": 52 + }, + { + "name": "TestSyncDnsRecords_UnresolvableRecordIsReportedAsDrift", + "kind": "fn", + "startLine": 142, + "endLine": 161, + "tokens": 193 + }, + { + "name": "TestSyncDnsRecords_AllRecordsVisibleIsVerified", + "kind": "fn", + "startLine": 163, + "endLine": 179, + "tokens": 138 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/sync_dns_records_test.go": { + "description": "fakeDnsProvider (26 fields); methods: GetZoneID, ListRecords, CreateRecord, UpdateRecord", + "tokens": 1060, + "hash": "6b3e8f3525129a61", + "size": 3708, + "mtimeMs": 1785635482523.9329, + "updatedAt": "2026-10-06T11:06:20.869Z", + "source": "scan", + "symbols": [ + { + "name": "fakeDnsProvider", + "kind": "class", + "startLine": 14, + "endLine": 17, + "tokens": 21 + }, + { + "name": "newFakeDnsProvider", + "kind": "fn", + "startLine": 19, + "endLine": 21, + "tokens": 21 + }, + { + "name": "seed", + "kind": "method", + "startLine": 24, + "endLine": 30, + "tokens": 53 + }, + { + "name": "find", + "kind": "method", + "startLine": 33, + "endLine": 41, + "tokens": 73 + }, + { + "name": "GetZoneID", + "kind": "method", + "startLine": 43, + "endLine": 45, + "tokens": 41 + }, + { + "name": "ListRecords", + "kind": "method", + "startLine": 47, + "endLine": 49, + "tokens": 45 + }, + { + "name": "CreateRecord", + "kind": "method", + "startLine": 51, + "endLine": 56, + "tokens": 60 + }, + { + "name": "UpdateRecord", + "kind": "method", + "startLine": 58, + "endLine": 66, + "tokens": 74 + }, + { + "name": "DeleteRecord", + "kind": "method", + "startLine": 68, + "endLine": 76, + "tokens": 69 + }, + { + "name": "fakeSystemAliasRepository", + "kind": "class", + "startLine": 78, + "endLine": 80, + "tokens": 16 + }, + { + "name": "EnsureSystemAliases", + "kind": "method", + "startLine": 82, + "endLine": 85, + "tokens": 39 + }, + { + "name": "TestSyncDnsRecordsUseCase_CreatesFullRecordSetAndAliases", + "kind": "fn", + "startLine": 91, + "endLine": 131, + "tokens": 379 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/sync_dns_records.go": { + "description": "SyncDnsRecordsInput (75 fields); methods: String, SetVerifier, Execute", + "tokens": 2481, + "hash": "7fa123df0d79faca", + "size": 8683, + "mtimeMs": 1785635482523.9329, + "updatedAt": "2026-10-06T11:06:20.869Z", + "source": "scan", + "symbols": [ + { + "name": "SyncStatus", + "kind": "class", + "startLine": 13, + "endLine": 13, + "tokens": 7 + }, + { + "name": "SyncDnsRecordsInput", + "kind": "class", + "startLine": 26, + "endLine": 41, + "tokens": 134 + }, + { + "name": "SyncConflict", + "kind": "class", + "startLine": 45, + "endLine": 50, + "tokens": 28 + }, + { + "name": "String", + "kind": "method", + "startLine": 52, + "endLine": 54, + "tokens": 40 + }, + { + "name": "SyncDnsRecordsOutput", + "kind": "class", + "startLine": 56, + "endLine": 68, + "tokens": 101 + }, + { + "name": "SyncDnsRecordsUseCase", + "kind": "class", + "startLine": 70, + "endLine": 74, + "tokens": 40 + }, + { + "name": "NewSyncDnsRecordsUseCase", + "kind": "fn", + "startLine": 76, + "endLine": 81, + "tokens": 61 + }, + { + "name": "SetVerifier", + "kind": "method", + "startLine": 87, + "endLine": 89, + "tokens": 29 + }, + { + "name": "Execute", + "kind": "method", + "startLine": 91, + "endLine": 188, + "tokens": 895 + }, + { + "name": "verifyPublished", + "kind": "method", + "startLine": 192, + "endLine": 210, + "tokens": 132 + }, + { + "name": "recordKey", + "kind": "fn", + "startLine": 212, + "endLine": 214, + "tokens": 39 + }, + { + "name": "classifyCandidates", + "kind": "fn", + "startLine": 220, + "endLine": 230, + "tokens": 100 + }, + { + "name": "isOurRecord", + "kind": "fn", + "startLine": 236, + "endLine": 247, + "tokens": 136 + }, + { + "name": "txtTag", + "kind": "fn", + "startLine": 252, + "endLine": 262, + "tokens": 84 + }, + { + "name": "allowsCoexistence", + "kind": "fn", + "startLine": 267, + "endLine": 274, + "tokens": 44 + } + ], + "symbolSource": "ts", + "skeleton": "type SyncStatus string\ntype SyncDnsRecordsInput struct {\ntype SyncConflict struct {\nfunc (c SyncConflict) String() string {\ntype SyncDnsRecordsOutput struct {\ntype SyncDnsRecordsUseCase struct {\nfunc NewSyncDnsRecordsUseCase(dnsProvider port.DnsProvider, aliasRepo port.SystemAliasRepository) *SyncDnsRecordsUseCase {\nfunc (uc *SyncDnsRecordsUseCase) SetVerifier(verifier port.DnsVerifier) {\nfunc (uc *SyncDnsRecordsUseCase) Execute(ctx context.Context, input SyncDnsRecordsInput) (*SyncDnsRecordsOutput, error) {\nfunc (uc *SyncDnsRecordsUseCase) verifyPublished(ctx context.Context, specs []entity.DnsRecord) []string {\nfunc recordKey(recType, name string) string {\nfunc classifyCandidates(spec entity.DnsRecord, candidates []entity.DnsRecord) (match *entity.DnsRecord, blocking *entity.DnsRecord) {\nfunc isOurRecord(spec, existing entity.DnsRecord) bool {\nfunc txtTag(value string) string {\nfunc allowsCoexistence(recType string) bool {", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/vacation_reply_test.go": { + "description": "TestReplyNamesTheMessageItAnswers, TestReplyCarriesTheConversationChain, TestReplyStartsTheChainWhenThereIsNone, TestReplyOmitsThreadingWhenThereIs...", + "tokens": 3030, + "hash": "f14b7dbf3c711018", + "size": 10605, + "mtimeMs": 1787422324480.9006, + "updatedAt": "2026-10-06T11:06:20.869Z", + "source": "scan", + "symbols": [ + { + "name": "vacationHeaders", + "kind": "fn", + "startLine": 24, + "endLine": 36, + "tokens": 127 + }, + { + "name": "replyTo", + "kind": "fn", + "startLine": 38, + "endLine": 46, + "tokens": 88 + }, + { + "name": "TestReplyNamesTheMessageItAnswers", + "kind": "fn", + "startLine": 48, + "endLine": 54, + "tokens": 78 + }, + { + "name": "TestReplyCarriesTheConversationChain", + "kind": "fn", + "startLine": 58, + "endLine": 72, + "tokens": 173 + }, + { + "name": "TestReplyStartsTheChainWhenThereIsNone", + "kind": "fn", + "startLine": 76, + "endLine": 82, + "tokens": 76 + }, + { + "name": "TestReplyOmitsThreadingWhenThereIsNothingToThreadOn", + "kind": "fn", + "startLine": 87, + "endLine": 96, + "tokens": 104 + }, + { + "name": "TestSubjectShowsItIsAReplyToTheirMessage", + "kind": "fn", + "startLine": 102, + "endLine": 112, + "tokens": 120 + }, + { + "name": "TestSubjectDoesNotStackReplyPrefixes", + "kind": "fn", + "startLine": 115, + "endLine": 123, + "tokens": 91 + }, + { + "name": "TestSubjectFallsBackToTheConfiguredText", + "kind": "fn", + "startLine": 127, + "endLine": 134, + "tokens": 76 + }, + { + "name": "TestReplyStillMarksItselfAutomatic", + "kind": "fn", + "startLine": 138, + "endLine": 147, + "tokens": 97 + }, + { + "name": "TestMalformedMessageIdIsBracketed", + "kind": "fn", + "startLine": 151, + "endLine": 157, + "tokens": 74 + }, + { + "name": "TestThreadingHeadersCannotInjectMoreHeaders", + "kind": "fn", + "startLine": 161, + "endLine": 167, + "tokens": 74 + }, + { + "name": "TestDeliveredMessageProducesAThreadedReply", + "kind": "fn", + "startLine": 173, + "endLine": 220, + "tokens": 534 + }, + { + "name": "TestOriginalSubjectIsDecodedBeforeUse", + "kind": "fn", + "startLine": 228, + "endLine": 241, + "tokens": 130 + }, + { + "name": "TestEncodedReplyPrefixIsNotDuplicated", + "kind": "fn", + "startLine": 244, + "endLine": 253, + "tokens": 120 + }, + { + "name": "TestManyReplyPrefixesCollapseToOne", + "kind": "fn", + "startLine": 257, + "endLine": 268, + "tokens": 131 + }, + { + "name": "TestReplyToTheAutoReplyDoesNotEchoItself", + "kind": "fn", + "startLine": 273, + "endLine": 282, + "tokens": 116 + } + ], + "symbolSource": "ts", + "skeleton": "func vacationHeaders(t *testing.T, reply pendingVacation) map[string]string {\nfunc replyTo(original, references string) pendingVacation {\nfunc TestReplyNamesTheMessageItAnswers(t *testing.T) {\nfunc TestReplyCarriesTheConversationChain(t *testing.T) {\nfunc TestReplyStartsTheChainWhenThereIsNone(t *testing.T) {\nfunc TestReplyOmitsThreadingWhenThereIsNothingToThreadOn(t *testing.T) {\nfunc TestSubjectShowsItIsAReplyToTheirMessage(t *testing.T) {\nfunc TestSubjectDoesNotStackReplyPrefixes(t *testing.T) {\nfunc TestSubjectFallsBackToTheConfiguredText(t *testing.T) {\nfunc TestReplyStillMarksItselfAutomatic(t *testing.T) {\nfunc TestMalformedMessageIdIsBracketed(t *testing.T) {\nfunc TestThreadingHeadersCannotInjectMoreHeaders(t *testing.T) {\nfunc TestDeliveredMessageProducesAThreadedReply(t *testing.T) {\nfunc TestOriginalSubjectIsDecodedBeforeUse(t *testing.T) {\nfunc TestEncodedReplyPrefixIsNotDuplicated(t *testing.T) {\nfunc TestManyReplyPrefixesCollapseToOne(t *testing.T) {\nfunc TestReplyToTheAutoReplyDoesNotEchoItself(t *testing.T) {", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/vacation_reply.go": { + "description": "pendingVacation (42 fields); methods: SetVacationSender, SetVacationSuppression", + "tokens": 2544, + "hash": "c3b3172e7aafbf04", + "size": 8901, + "mtimeMs": 1787422474764.3455, + "updatedAt": "2026-10-06T11:06:20.869Z", + "source": "scan", + "symbols": [ + { + "name": "pendingVacation", + "kind": "class", + "startLine": 19, + "endLine": 32, + "tokens": 129 + }, + { + "name": "SetVacationSender", + "kind": "method", + "startLine": 37, + "endLine": 43, + "tokens": 60 + }, + { + "name": "SetVacationSuppression", + "kind": "method", + "startLine": 47, + "endLine": 49, + "tokens": 31 + }, + { + "name": "sendVacationReplies", + "kind": "method", + "startLine": 56, + "endLine": 77, + "tokens": 217 + }, + { + "name": "sendVacationReply", + "kind": "method", + "startLine": 79, + "endLine": 97, + "tokens": 192 + }, + { + "name": "buildVacationMessage", + "kind": "fn", + "startLine": 112, + "endLine": 141, + "tokens": 383 + }, + { + "name": "vacationSubject", + "kind": "fn", + "startLine": 148, + "endLine": 171, + "tokens": 254 + }, + { + "name": "decodeHeaderValue", + "kind": "fn", + "startLine": 178, + "endLine": 188, + "tokens": 68 + }, + { + "name": "stripReplyPrefixes", + "kind": "fn", + "startLine": 194, + "endLine": 209, + "tokens": 114 + }, + { + "name": "messageIDRef", + "kind": "fn", + "startLine": 216, + "endLine": 228, + "tokens": 72 + }, + { + "name": "buildReferences", + "kind": "fn", + "startLine": 232, + "endLine": 251, + "tokens": 199 + } + ], + "symbolSource": "ts", + "skeleton": "type pendingVacation struct {\nfunc (uc *ProcessInboundEmailUseCase) SetVacationSender(\nfunc (uc *ProcessInboundEmailUseCase) SetVacationSuppression(s *VacationSuppression) {\nfunc (uc *ProcessInboundEmailUseCase) sendVacationReplies(ctx context.Context, replies []pendingVacation) {\nfunc (uc *ProcessInboundEmailUseCase) sendVacationReply(ctx context.Context, reply pendingVacation) error {\nfunc buildVacationMessage(reply pendingVacation, host string) []byte {\nfunc vacationSubject(reply pendingVacation) string {\nfunc decodeHeaderValue(value string) string {\nfunc stripReplyPrefixes(subject string) string {\nfunc messageIDRef(raw string) string {\nfunc buildReferences(existing, original string) string {", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/vacation_suppression_test.go": { + "description": "stubVacationLog (18 fields); methods: LastRepliedAt, RecordReply", + "tokens": 1409, + "hash": "2e99c474aca4d8fa", + "size": 4930, + "mtimeMs": 1787422459237.1782, + "updatedAt": "2026-10-06T11:06:20.869Z", + "source": "scan", + "symbols": [ + { + "name": "stubVacationLog", + "kind": "class", + "startLine": 19, + "endLine": 23, + "tokens": 26 + }, + { + "name": "LastRepliedAt", + "kind": "method", + "startLine": 25, + "endLine": 30, + "tokens": 53 + }, + { + "name": "RecordReply", + "kind": "method", + "startLine": 32, + "endLine": 35, + "tokens": 40 + }, + { + "name": "newSuppression", + "kind": "fn", + "startLine": 37, + "endLine": 39, + "tokens": 34 + }, + { + "name": "TestFirstMessageFromSomeoneGetsAReply", + "kind": "fn", + "startLine": 41, + "endLine": 46, + "tokens": 78 + }, + { + "name": "TestSecondMessageWithinThePeriodIsSilent", + "kind": "fn", + "startLine": 50, + "endLine": 57, + "tokens": 97 + }, + { + "name": "TestSomeoneWritingAfterThePeriodIsToldAgain", + "kind": "fn", + "startLine": 61, + "endLine": 68, + "tokens": 100 + }, + { + "name": "TestDifferentPeopleEachGetOne", + "kind": "fn", + "startLine": 70, + "endLine": 77, + "tokens": 84 + }, + { + "name": "TestAddressComparisonIgnoresCase", + "kind": "fn", + "startLine": 80, + "endLine": 87, + "tokens": 89 + }, + { + "name": "TestAnUnreadableLogStillReplies", + "kind": "fn", + "startLine": 91, + "endLine": 96, + "tokens": 74 + }, + { + "name": "TestNoLogMeansNoSuppression", + "kind": "fn", + "startLine": 99, + "endLine": 103, + "tokens": 60 + }, + { + "name": "TestReplyIsRecorded", + "kind": "fn", + "startLine": 105, + "endLine": 113, + "tokens": 93 + }, + { + "name": "errTestUnavailable", + "kind": "class", + "startLine": 117, + "endLine": 117, + "tokens": 10 + }, + { + "name": "Error", + "kind": "method", + "startLine": 119, + "endLine": 119, + "tokens": 20 + }, + { + "name": "TestDeliveryTellsEachSenderOnlyOnce", + "kind": "fn", + "startLine": 125, + "endLine": 139, + "tokens": 176 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/vacation_suppression.go": { + "description": "VacationSuppression (19 fields); methods: ShouldReply, Record", + "tokens": 778, + "hash": "6d157a1fd24d58d7", + "size": 2720, + "mtimeMs": 1787422476112.9192, + "updatedAt": "2026-10-06T11:06:20.869Z", + "source": "scan", + "symbols": [ + { + "name": "VacationSuppression", + "kind": "class", + "startLine": 23, + "endLine": 26, + "tokens": 24 + }, + { + "name": "NewVacationSuppression", + "kind": "fn", + "startLine": 31, + "endLine": 36, + "tokens": 64 + }, + { + "name": "ShouldReply", + "kind": "method", + "startLine": 43, + "endLine": 61, + "tokens": 140 + }, + { + "name": "Record", + "kind": "method", + "startLine": 64, + "endLine": 77, + "tokens": 136 + }, + { + "name": "normaliseAddress", + "kind": "fn", + "startLine": 80, + "endLine": 88, + "tokens": 89 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/webmail_compose_test.go": { + "description": "stubWebmailRepo (47 fields); methods: FindMailboxIDByAddress, ListFolders, ListMessages, GetMessageBlob", + "tokens": 4946, + "hash": "d9c4463cfd99a0f0", + "size": 17309, + "mtimeMs": 1788779887232.175, + "updatedAt": "2026-10-06T11:06:20.870Z", + "source": "scan", + "symbols": [ + { + "name": "TestComposedMessageCarriesTheBody", + "kind": "fn", + "startLine": 18, + "endLine": 32, + "tokens": 176 + }, + { + "name": "TestHtmlIsSentAsHtml", + "kind": "fn", + "startLine": 34, + "endLine": 60, + "tokens": 346 + }, + { + "name": "TestPlainTextOnlyMessageStaysSimple", + "kind": "fn", + "startLine": 62, + "endLine": 76, + "tokens": 141 + }, + { + "name": "TestBccNeverAppearsInTheHeaders", + "kind": "fn", + "startLine": 78, + "endLine": 120, + "tokens": 475 + }, + { + "name": "TestHtmlToPlainTextIsReadable", + "kind": "fn", + "startLine": 122, + "endLine": 132, + "tokens": 139 + }, + { + "name": "stubWebmailRepo", + "kind": "class", + "startLine": 136, + "endLine": 143, + "tokens": 46 + }, + { + "name": "FindMailboxIDByAddress", + "kind": "method", + "startLine": 145, + "endLine": 147, + "tokens": 34 + }, + { + "name": "ListFolders", + "kind": "method", + "startLine": 148, + "endLine": 150, + "tokens": 33 + }, + { + "name": "ListMessages", + "kind": "method", + "startLine": 151, + "endLine": 153, + "tokens": 41 + }, + { + "name": "GetMessageBlob", + "kind": "method", + "startLine": 154, + "endLine": 156, + "tokens": 36 + }, + { + "name": "MarkSeen", + "kind": "method", + "startLine": 157, + "endLine": 157, + "tokens": 30 + }, + { + "name": "Expunge", + "kind": "method", + "startLine": 158, + "endLine": 161, + "tokens": 42 + }, + { + "name": "MoveToFolder", + "kind": "method", + "startLine": 162, + "endLine": 170, + "tokens": 76 + }, + { + "name": "MoveToTrash", + "kind": "method", + "startLine": 172, + "endLine": 178, + "tokens": 61 + }, + { + "name": "newSendableUseCase", + "kind": "fn", + "startLine": 182, + "endLine": 185, + "tokens": 56 + }, + { + "name": "newSendableUseCaseWith", + "kind": "fn", + "startLine": 187, + "endLine": 204, + "tokens": 198 + }, + { + "name": "stubAuthRepo", + "kind": "class", + "startLine": 206, + "endLine": 206, + "tokens": 16 + }, + { + "name": "FindByAddress", + "kind": "method", + "startLine": 208, + "endLine": 210, + "tokens": 33 + }, + { + "name": "TestDraftAutosaveReplacesThePreviousOne", + "kind": "fn", + "startLine": 214, + "endLine": 251, + "tokens": 400 + }, + { + "name": "TestDraftSavesWithoutRecipients", + "kind": "fn", + "startLine": 255, + "endLine": 271, + "tokens": 187 + }, + { + "name": "TestSendingDiscardsTheDraftItCameFrom", + "kind": "fn", + "startLine": 278, + "endLine": 296, + "tokens": 169 + }, + { + "name": "TestSendingWithoutADraftExpungesNothing", + "kind": "fn", + "startLine": 300, + "endLine": 313, + "tokens": 135 + }, + { + "name": "TestSentCopyIsFiledAsAlreadyRead", + "kind": "fn", + "startLine": 317, + "endLine": 335, + "tokens": 178 + }, + { + "name": "TestDraftIsFiledAsAlreadyRead", + "kind": "fn", + "startLine": 339, + "endLine": 357, + "tokens": 214 + }, + { + "name": "TestDeleteMovesToTrash", + "kind": "fn", + "startLine": 362, + "endLine": 380, + "tokens": 200 + }, + { + "name": "TestDeleteFromTrashExpunges", + "kind": "fn", + "startLine": 383, + "endLine": 398, + "tokens": 178 + }, + { + "name": "TestMoveFilesTheMessageInTheTargetFolder", + "kind": "fn", + "startLine": 404, + "endLine": 419, + "tokens": 161 + }, + { + "name": "TestMoveRefusesSentAndDrafts", + "kind": "fn", + "startLine": 425, + "endLine": 443, + "tokens": 201 + }, + { + "name": "TestMoveIntoTheSameFolderDoesNothing", + "kind": "fn", + "startLine": 447, + "endLine": 462, + "tokens": 165 + }, + { + "name": "TestMoveRejectsAnEmptyTarget", + "kind": "fn", + "startLine": 464, + "endLine": 474, + "tokens": 128 + } + ], + "symbolSource": "ts", + "skeleton": "func TestComposedMessageCarriesTheBody(t *testing.T) {\nfunc TestHtmlIsSentAsHtml(t *testing.T) {\nfunc TestPlainTextOnlyMessageStaysSimple(t *testing.T) {\nfunc TestBccNeverAppearsInTheHeaders(t *testing.T) {\nfunc TestHtmlToPlainTextIsReadable(t *testing.T) {\ntype stubWebmailRepo struct {\nfunc (s *stubWebmailRepo) FindMailboxIDByAddress(context.Context, string) (string, error) {\nfunc (s *stubWebmailRepo) ListFolders(context.Context, string) ([]port.WebmailFolder, error) {\nfunc (s *stubWebmailRepo) ListMessages(context.Context, string, string, string, int, int) ([]port.WebmailMessage, error) {\nfunc (s *stubWebmailRepo) GetMessageBlob(context.Context, string, string, uint32) (uuid.UUID, error) {\nfunc (s *stubWebmailRepo) MarkSeen(context.Context, string, string, uint32, bool) error { return nil }\nfunc (s *stubWebmailRepo) Expunge(_ context.Context, _, _ string, uid uint32) error {\nfunc (s *stubWebmailRepo) MoveToFolder(\nfunc (s *stubWebmailRepo) MoveToTrash(_ context.Context, _, _ string, uid uint32) (uint32, error) {\nfunc newSendableUseCase(t *testing.T, mailboxID uuid.UUID, repo *stubWebmailRepo) *WebmailUseCase {\nfunc newSendableUseCaseWith(\ntype stubAuthRepo struct{ account *port.MailboxAuth }\nfunc (s *stubAuthRepo) FindByAddress(context.Context, string) (*port.MailboxAuth, error) {\nfunc TestDraftAutosaveReplacesThePreviousOne(t *testing.T) {", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/application/usecase/webmail_mail.go": { + "description": "WebmailUseCase (81 fields); methods: WithLocalFiling, MailboxIDFor, ListFolders, ListMessages", + "tokens": 5136, + "hash": "8879750a0e9acb75", + "size": 17976, + "mtimeMs": 1788779917943.6719, + "updatedAt": "2026-10-06T11:06:20.870Z", + "source": "scan", + "symbols": [ + { + "name": "WebmailUseCase", + "kind": "class", + "startLine": 29, + "endLine": 41, + "tokens": 152 + }, + { + "name": "NewWebmailUseCase", + "kind": "fn", + "startLine": 43, + "endLine": 54, + "tokens": 99 + }, + { + "name": "WithLocalFiling", + "kind": "method", + "startLine": 57, + "endLine": 61, + "tokens": 52 + }, + { + "name": "MailboxIDFor", + "kind": "method", + "startLine": 65, + "endLine": 67, + "tokens": 38 + }, + { + "name": "mailboxID", + "kind": "method", + "startLine": 69, + "endLine": 75, + "tokens": 65 + }, + { + "name": "ListFolders", + "kind": "method", + "startLine": 77, + "endLine": 83, + "tokens": 64 + }, + { + "name": "ListMessages", + "kind": "method", + "startLine": 85, + "endLine": 93, + "tokens": 85 + }, + { + "name": "GetMessage", + "kind": "method", + "startLine": 96, + "endLine": 116, + "tokens": 156 + }, + { + "name": "SetSeen", + "kind": "method", + "startLine": 118, + "endLine": 124, + "tokens": 68 + }, + { + "name": "ComposeInput", + "kind": "class", + "startLine": 127, + "endLine": 148, + "tokens": 263 + }, + { + "name": "Send", + "kind": "method", + "startLine": 157, + "endLine": 208, + "tokens": 563 + }, + { + "name": "Move", + "kind": "method", + "startLine": 229, + "endLine": 249, + "tokens": 179 + }, + { + "name": "Delete", + "kind": "method", + "startLine": 257, + "endLine": 274, + "tokens": 161 + }, + { + "name": "fileLocalCopy", + "kind": "method", + "startLine": 279, + "endLine": 304, + "tokens": 229 + }, + { + "name": "SaveDraft", + "kind": "method", + "startLine": 316, + "endLine": 371, + "tokens": 516 + }, + { + "name": "normaliseRecipients", + "kind": "fn", + "startLine": 377, + "endLine": 393, + "tokens": 127 + }, + { + "name": "buildMimeMessage", + "kind": "fn", + "startLine": 406, + "endLine": 453, + "tokens": 478 + }, + { + "name": "normaliseEOL", + "kind": "fn", + "startLine": 457, + "endLine": 463, + "tokens": 51 + }, + { + "name": "htmlToPlainText", + "kind": "fn", + "startLine": 471, + "endLine": 501, + "tokens": 220 + }, + { + "name": "encodeHeaderValue", + "kind": "fn", + "startLine": 505, + "endLine": 513, + "tokens": 78 + }, + { + "name": "mimeEncode", + "kind": "fn", + "startLine": 516, + "endLine": 519, + "tokens": 44 + } + ], + "symbolSource": "ts", + "skeleton": "type WebmailUseCase struct {\nfunc NewWebmailUseCase(\nfunc (uc *WebmailUseCase) WithLocalFiling(store port.BlobStorage, messages port.InboundMessageRepository) *WebmailUseCase {\nfunc (uc *WebmailUseCase) MailboxIDFor(ctx context.Context, address string) (string, error) {\nfunc (uc *WebmailUseCase) mailboxID(ctx context.Context, address string) (string, error) {\nfunc (uc *WebmailUseCase) ListFolders(ctx context.Context, address string) ([]port.WebmailFolder, error) {\nfunc (uc *WebmailUseCase) ListMessages(\nfunc (uc *WebmailUseCase) GetMessage(ctx context.Context, address, folder string, uid uint32) ([]byte, error) {\nfunc (uc *WebmailUseCase) SetSeen(ctx context.Context, address, folder string, uid uint32, seen bool) error {\ntype ComposeInput struct {\nfunc (uc *WebmailUseCase) Send(ctx context.Context, input ComposeInput) error {\nfunc (uc *WebmailUseCase) Move(ctx context.Context, address, folder string, uid uint32, target string) error {\nfunc (uc *WebmailUseCase) Delete(ctx context.Context, address, folder string, uid uint32) error {\nfunc (uc *WebmailUseCase) fileLocalCopy(\nfunc (uc *WebmailUseCase) SaveDraft(ctx context.Context, input ComposeInput, replaceUID uint32) (uint32, error) {\nfunc normaliseRecipients(raw []string) []string {\nfunc buildMimeMessage(input ComposeInput, recipients []string, localHost string) []byte {\nfunc normaliseEOL(s string) string {", + "imports": [ + "services/webmail/src/app/(user)/user/mail/[folder]/page.tsx", + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/domain/bimi/svg_test.go": { + "description": "TestValidate_AcceptsAConformingLogo, TestValidate_RequiresTheProfileReceiversLookFor, TestValidate_RequiresASquareViewBox, TestValidate_RequiresExa...", + "tokens": 1630, + "hash": "f6a6c12c0649ee0b", + "size": 5704, + "mtimeMs": 1788830725027.3508, + "updatedAt": "2026-10-06T11:06:20.870Z", + "source": "scan", + "symbols": [ + { + "name": "rulesOf", + "kind": "fn", + "startLine": 18, + "endLine": 24, + "tokens": 41 + }, + { + "name": "contains", + "kind": "fn", + "startLine": 26, + "endLine": 33, + "tokens": 38 + }, + { + "name": "TestValidate_AcceptsAConformingLogo", + "kind": "fn", + "startLine": 35, + "endLine": 39, + "tokens": 48 + }, + { + "name": "TestValidate_RequiresTheProfileReceiversLookFor", + "kind": "fn", + "startLine": 41, + "endLine": 59, + "tokens": 220 + }, + { + "name": "TestValidate_RequiresASquareViewBox", + "kind": "fn", + "startLine": 63, + "endLine": 78, + "tokens": 204 + }, + { + "name": "TestValidate_RequiresExactlyOneTitle", + "kind": "fn", + "startLine": 80, + "endLine": 95, + "tokens": 184 + }, + { + "name": "TestValidate_RefusesAnythingActiveOrExternal", + "kind": "fn", + "startLine": 99, + "endLine": 119, + "tokens": 317 + }, + { + "name": "TestValidate_RefusesWhatCannotBeReadAtAll", + "kind": "fn", + "startLine": 121, + "endLine": 132, + "tokens": 126 + }, + { + "name": "TestValidate_RefusesALogoTooBigToBeFetched", + "kind": "fn", + "startLine": 134, + "endLine": 140, + "tokens": 84 + }, + { + "name": "TestValidate_ReportsEveryViolationAtOnce", + "kind": "fn", + "startLine": 144, + "endLine": 152, + "tokens": 109 + } + ], + "symbolSource": "ts", + "importance": 0.023 + }, + "services/protocols/internal/domain/bimi/svg.go": { + "description": "validates the logo a domain publishes for BIMI.", + "tokens": 1575, + "hash": "17e6c1f8c3fc869f", + "size": 5511, + "mtimeMs": 1788830725027.1436, + "updatedAt": "2026-10-06T11:06:20.870Z", + "source": "scan", + "symbols": [ + { + "name": "Violation", + "kind": "class", + "startLine": 22, + "endLine": 25, + "tokens": 16 + }, + { + "name": "String", + "kind": "method", + "startLine": 27, + "endLine": 27, + "tokens": 25 + }, + { + "name": "Validate", + "kind": "fn", + "startLine": 46, + "endLine": 127, + "tokens": 788 + }, + { + "name": "checkSquareViewBox", + "kind": "fn", + "startLine": 130, + "endLine": 153, + "tokens": 233 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/domain/entity/dkim_key_pair_test.go": { + "description": "TestNewDkimKeyPair_StartsPending, TestDkimKeyPair_Activate_SetsActive", + "tokens": 139, + "hash": "9a9aa453eafe53aa", + "size": 486, + "mtimeMs": 1785635482524.3235, + "updatedAt": "2026-10-06T11:06:20.870Z", + "source": "scan", + "importance": 0.2974 + }, + "services/protocols/internal/domain/entity/dkim_key_pair.go": { + "description": "DkimStatus is the lifecycle state of a DkimKeyPair (PLAN.md section 9, dkim_keys.status).", + "tokens": 317, + "hash": "ae4f77b697b11151", + "size": 1109, + "mtimeMs": 1785635482524.2761, + "updatedAt": "2026-10-06T11:06:20.870Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/entity/dmarc_report_test.go": { + "description": "TestParseDmarcXmlReport_ValidXML, TestParseTlsRptReport_ValidJSON", + "tokens": 659, + "hash": "de1ba18aff54e9ca", + "size": 2304, + "mtimeMs": 1787249227615.333, + "updatedAt": "2026-10-06T11:06:20.871Z", + "source": "scan", + "symbols": [ + { + "name": "TestParseDmarcXmlReport_ValidXML", + "kind": "fn", + "startLine": 9, + "endLine": 61, + "tokens": 393 + }, + { + "name": "TestParseTlsRptReport_ValidJSON", + "kind": "fn", + "startLine": 63, + "endLine": 96, + "tokens": 247 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/domain/entity/dmarc_report.go": { + "description": "DmarcRecord (59 fields)", + "tokens": 1261, + "hash": "d6596825dc92a8da", + "size": 4412, + "mtimeMs": 1787315035655.3943, + "updatedAt": "2026-10-06T11:06:20.871Z", + "source": "scan", + "symbols": [ + { + "name": "DmarcRecord", + "kind": "class", + "startLine": 13, + "endLine": 20, + "tokens": 42 + }, + { + "name": "DmarcReport", + "kind": "class", + "startLine": 22, + "endLine": 29, + "tokens": 51 + }, + { + "name": "dmarcXmlReport", + "kind": "class", + "startLine": 31, + "endLine": 57, + "tokens": 215 + }, + { + "name": "ParseDmarcXmlReport", + "kind": "fn", + "startLine": 65, + "endLine": 112, + "tokens": 387 + }, + { + "name": "isGzip", + "kind": "fn", + "startLine": 114, + "endLine": 116, + "tokens": 31 + }, + { + "name": "isZip", + "kind": "fn", + "startLine": 121, + "endLine": 124, + "tokens": 43 + }, + { + "name": "firstFileInZip", + "kind": "fn", + "startLine": 130, + "endLine": 154, + "tokens": 237 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/domain/entity/dmarc_zip_test.go": { + "description": "TestDmarcReportParsesZippedPayload, TestDmarcReportStillParsesPlainXml, TestDmarcReportRejectsEmptyZip", + "tokens": 708, + "hash": "e72ccc004af3e1ce", + "size": 2477, + "mtimeMs": 1787315014436.6196, + "updatedAt": "2026-10-06T11:06:20.871Z", + "source": "scan", + "symbols": [ + { + "name": "TestDmarcReportParsesZippedPayload", + "kind": "fn", + "startLine": 34, + "endLine": 61, + "tokens": 213 + }, + { + "name": "TestDmarcReportStillParsesPlainXml", + "kind": "fn", + "startLine": 64, + "endLine": 72, + "tokens": 74 + }, + { + "name": "TestDmarcReportRejectsEmptyZip", + "kind": "fn", + "startLine": 76, + "endLine": 85, + "tokens": 79 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/domain/entity/dns_record_idempotency_test.go": { + "description": "TestBuildDnsRecordSpecs_MtaStsIdIsStableAcrossCalls, TestBuildDnsRecordSpecs_MtaStsIdChangesWithPolicy, TestDnsRecord_EqualsNormalized_DetectsPrior...", + "tokens": 740, + "hash": "d5b7a39df88233d3", + "size": 2588, + "mtimeMs": 1785635482524.3235, + "updatedAt": "2026-10-06T11:06:20.871Z", + "source": "scan", + "symbols": [ + { + "name": "buildTestSpecs", + "kind": "fn", + "startLine": 7, + "endLine": 15, + "tokens": 74 + }, + { + "name": "findRecord", + "kind": "fn", + "startLine": 17, + "endLine": 26, + "tokens": 74 + }, + { + "name": "TestBuildDnsRecordSpecs_MtaStsIdIsStableAcrossCalls", + "kind": "fn", + "startLine": 32, + "endLine": 39, + "tokens": 100 + }, + { + "name": "TestBuildDnsRecordSpecs_MtaStsIdChangesWithPolicy", + "kind": "fn", + "startLine": 43, + "endLine": 58, + "tokens": 160 + }, + { + "name": "TestDnsRecord_EqualsNormalized_DetectsPriorityDrift", + "kind": "fn", + "startLine": 62, + "endLine": 73, + "tokens": 168 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/domain/entity/dns_record_test.go": { + "description": "TestBuildDnsRecordSpecs_GeneratesMandatoryRecords, TestDkimRecordsAreOmittedWhenThereIsNoKey, TestDkimRecordsArePublishedWhenKeysExist, TestOnlyThe...", + "tokens": 2424, + "hash": "149f9809d9cee0e5", + "size": 8484, + "mtimeMs": 1788830725027.967, + "updatedAt": "2026-10-06T11:06:20.871Z", + "source": "scan", + "symbols": [ + { + "name": "TestBuildDnsRecordSpecs_GeneratesMandatoryRecords", + "kind": "fn", + "startLine": 8, + "endLine": 71, + "tokens": 548 + }, + { + "name": "TestDkimRecordsAreOmittedWhenThereIsNoKey", + "kind": "fn", + "startLine": 77, + "endLine": 92, + "tokens": 150 + }, + { + "name": "TestDkimRecordsArePublishedWhenKeysExist", + "kind": "fn", + "startLine": 94, + "endLine": 118, + "tokens": 216 + }, + { + "name": "TestOnlyTheAvailableDkimKeyIsPublished", + "kind": "fn", + "startLine": 122, + "endLine": 134, + "tokens": 108 + }, + { + "name": "TestBuildDnsRecordSpecs_SkipsMailHostAddressesOutsideTheDomain", + "kind": "fn", + "startLine": 145, + "endLine": 173, + "tokens": 245 + }, + { + "name": "TestBuildDnsRecordSpecs_KeepsMailHostAddressesInsideTheDomain", + "kind": "fn", + "startLine": 177, + "endLine": 197, + "tokens": 154 + }, + { + "name": "TestBuildDnsRecordSpecs_BimiOnlyWhenThereIsALogo", + "kind": "fn", + "startLine": 204, + "endLine": 243, + "tokens": 350 + }, + { + "name": "TestBuildDnsRecordSpecs_BimiCarriesTheCertificateWhenConfigured", + "kind": "fn", + "startLine": 247, + "endLine": 266, + "tokens": 195 + } + ], + "symbolSource": "ts", + "skeleton": "func TestBuildDnsRecordSpecs_GeneratesMandatoryRecords(t *testing.T) {\nfunc TestDkimRecordsAreOmittedWhenThereIsNoKey(t *testing.T) {\nfunc TestDkimRecordsArePublishedWhenKeysExist(t *testing.T) {\nfunc TestOnlyTheAvailableDkimKeyIsPublished(t *testing.T) {\nfunc TestBuildDnsRecordSpecs_SkipsMailHostAddressesOutsideTheDomain(t *testing.T) {\nfunc TestBuildDnsRecordSpecs_KeepsMailHostAddressesInsideTheDomain(t *testing.T) {\nfunc TestBuildDnsRecordSpecs_BimiOnlyWhenThereIsALogo(t *testing.T) {\nfunc TestBuildDnsRecordSpecs_BimiCarriesTheCertificateWhenConfigured(t *testing.T) {", + "importance": 0.0144 + }, + "services/protocols/internal/domain/entity/dns_record.go": { + "description": "DnsRecord (47 fields); methods: EqualsNormalized", + "tokens": 2951, + "hash": "1b6d06eaba213199", + "size": 10326, + "mtimeMs": 1788830725027.7786, + "updatedAt": "2026-10-06T11:06:20.871Z", + "source": "scan", + "symbols": [ + { + "name": "DnsRecord", + "kind": "class", + "startLine": 10, + "endLine": 19, + "tokens": 44 + }, + { + "name": "DnsRecordSpec", + "kind": "class", + "startLine": 22, + "endLine": 47, + "tokens": 302 + }, + { + "name": "BuildDnsRecordSpecs", + "kind": "fn", + "startLine": 52, + "endLine": 254, + "tokens": 1817 + }, + { + "name": "buildSpfRecord", + "kind": "fn", + "startLine": 259, + "endLine": 265, + "tokens": 71 + }, + { + "name": "intPtr", + "kind": "fn", + "startLine": 267, + "endLine": 269, + "tokens": 11 + }, + { + "name": "mtaStsPolicyID", + "kind": "fn", + "startLine": 277, + "endLine": 280, + "tokens": 53 + }, + { + "name": "EqualsNormalized", + "kind": "method", + "startLine": 283, + "endLine": 305, + "tokens": 173 + }, + { + "name": "hostInsideDomain", + "kind": "fn", + "startLine": 309, + "endLine": 318, + "tokens": 119 + } + ], + "symbolSource": "ts", + "skeleton": "type DnsRecord struct {\ntype DnsRecordSpec struct {\nfunc BuildDnsRecordSpecs(spec DnsRecordSpec) []DnsRecord {\nfunc buildSpfRecord(mailHost, relayInclude string) string {\nfunc intPtr(v int) *int {\nfunc mtaStsPolicyID(domainName, mailHost string) string {\nfunc (r DnsRecord) EqualsNormalized(other DnsRecord) bool {\nfunc hostInsideDomain(host, domain string) bool {", + "importance": 0.0144 + }, + "services/protocols/internal/domain/entity/domain_test.go": { + "description": "TestNewDomain_StartsActiveWithNoMailboxes, TestDomain_Deactivate_SucceedsWithNoActiveMailboxes, TestDomain_Deactivate_FailsWithActiveMailboxes", + "tokens": 308, + "hash": "36c30a788908ffe6", + "size": 1075, + "mtimeMs": 1785635482524.3235, + "updatedAt": "2026-10-06T11:06:20.871Z", + "source": "scan", + "imports": [ + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/domain/entity/domain.go": { + "description": "contains the domain's aggregate roots and entities (Clean Architecture layer 1).", + "tokens": 398, + "hash": "e7d3097ae00c1503", + "size": 1390, + "mtimeMs": 1785635482524.3235, + "updatedAt": "2026-10-06T11:06:20.871Z", + "source": "scan", + "imports": [ + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/domain/entity/folder_test.go": { + "description": "TestFolder_NextUID_StartsAtOneAndIsMonotonic", + "tokens": 141, + "hash": "ed7eafac95071177", + "size": 491, + "mtimeMs": 1785635482524.3235, + "updatedAt": "2026-10-06T11:06:20.872Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/entity/folder.go": { + "description": "Folder is an IMAP folder entity. uidNext is strictly monotonic (RFC 3501 section 2.3.1.1),", + "tokens": 168, + "hash": "99e91c5567e5a64f", + "size": 586, + "mtimeMs": 1785635482524.3235, + "updatedAt": "2026-10-06T11:06:20.872Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/entity/mailbox_test.go": { + "description": "TestNewMailbox_StartsWithZeroUsage, TestMailbox_RecordUsage_AllowsUpToQuota, TestMailbox_RecordUsage_RejectsOverQuota", + "tokens": 329, + "hash": "892e4df20939ef17", + "size": 1149, + "mtimeMs": 1785635482524.3235, + "updatedAt": "2026-10-06T11:06:20.872Z", + "source": "scan", + "imports": [ + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/domain/entity/mailbox.go": { + "description": "Mailbox (10 fields); methods: Address, UsedBytes, Quota, RecordUsage", + "tokens": 324, + "hash": "c114c865ddb098d2", + "size": 1132, + "mtimeMs": 1785635482524.3235, + "updatedAt": "2026-10-06T11:06:20.872Z", + "source": "scan", + "imports": [ + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/domain/entity/outbound_job_test.go": { + "description": "TestCalculateNextBackoff_FollowsRetryTable, TestCalculateNextBackoff_ExpiredJobIsPermanent", + "tokens": 532, + "hash": "5ae778fb8f634de6", + "size": 1859, + "mtimeMs": 1785635482524.3235, + "updatedAt": "2026-10-06T11:06:20.872Z", + "source": "scan", + "symbols": [ + { + "name": "TestCalculateNextBackoff_FollowsRetryTable", + "kind": "fn", + "startLine": 13, + "endLine": 47, + "tokens": 360 + }, + { + "name": "TestCalculateNextBackoff_ExpiredJobIsPermanent", + "kind": "fn", + "startLine": 49, + "endLine": 54, + "tokens": 65 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/domain/entity/outbound_job.go": { + "description": "OutboundJob (35 fields)", + "tokens": 705, + "hash": "a5b2fe3cc2bbc0da", + "size": 2466, + "mtimeMs": 1785635482524.3235, + "updatedAt": "2026-10-06T11:06:20.872Z", + "source": "scan", + "symbols": [ + { + "name": "OutboundJobStatus", + "kind": "class", + "startLine": 10, + "endLine": 10, + "tokens": 9 + }, + { + "name": "OutboundJob", + "kind": "class", + "startLine": 21, + "endLine": 37, + "tokens": 127 + }, + { + "name": "CalculateNextBackoff", + "kind": "fn", + "startLine": 44, + "endLine": 87, + "tokens": 332 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/domain/entity/sieve_script.go": { + "description": "SieveScript represents a user's Sieve filtering script entity.", + "tokens": 68, + "hash": "735cbfb2dea046e3", + "size": 237, + "mtimeMs": 1785635482524.3235, + "updatedAt": "2026-10-06T11:06:20.872Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/entity/sieve_validator_test.go": { + "description": "TestValidateSieveScript_ValidScripts, TestValidateSieveScript_InvalidScripts", + "tokens": 396, + "hash": "ead8ddb79cdf1f96", + "size": 1384, + "mtimeMs": 1785635482524.3235, + "updatedAt": "2026-10-06T11:06:20.872Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/entity/sieve_validator.go": { + "description": "ValidateSieveScript", + "tokens": 644, + "hash": "7e79c6ed4ac908b9", + "size": 2251, + "mtimeMs": 1785635482524.3235, + "updatedAt": "2026-10-06T11:06:20.872Z", + "source": "scan", + "symbols": [ + { + "name": "ValidateSieveScript", + "kind": "fn", + "startLine": 18, + "endLine": 117, + "tokens": 486 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/domain/entity/tls_policy_enforcement_test.go": { + "description": "TestTLSPolicy_DaneTakesPrecedenceOverMtaSts, TestTLSPolicy_OpportunisticDoesNotRequireValidation, TestTLSPolicy_MtaStsTestingDoesNotEnforce, TestTL...", + "tokens": 1571, + "hash": "ea380a98d0667574", + "size": 5498, + "mtimeMs": 1785635482524.3235, + "updatedAt": "2026-10-06T11:06:20.872Z", + "source": "scan", + "symbols": [ + { + "name": "selfSignedCert", + "kind": "fn", + "startLine": 18, + "endLine": 39, + "tokens": 190 + }, + { + "name": "TestTLSPolicy_DaneTakesPrecedenceOverMtaSts", + "kind": "fn", + "startLine": 42, + "endLine": 52, + "tokens": 128 + }, + { + "name": "TestTLSPolicy_OpportunisticDoesNotRequireValidation", + "kind": "fn", + "startLine": 56, + "endLine": 60, + "tokens": 58 + }, + { + "name": "TestTLSPolicy_MtaStsTestingDoesNotEnforce", + "kind": "fn", + "startLine": 64, + "endLine": 74, + "tokens": 106 + }, + { + "name": "TestTLSPolicy_DaneAlwaysEnforces", + "kind": "fn", + "startLine": 77, + "endLine": 81, + "tokens": 47 + }, + { + "name": "TestTLSPolicy_CoversHostAppliesSingleLabelWildcard", + "kind": "fn", + "startLine": 84, + "endLine": 102, + "tokens": 162 + }, + { + "name": "TestTLSPolicy_OpportunisticCoversEveryHost", + "kind": "fn", + "startLine": 105, + "endLine": 109, + "tokens": 56 + }, + { + "name": "TestTLSPolicy_MatchesCertificateAgainstTlsaDigest", + "kind": "fn", + "startLine": 113, + "endLine": 131, + "tokens": 196 + }, + { + "name": "TestTLSPolicy_MatchesEitherRolloverRecord", + "kind": "fn", + "startLine": 135, + "endLine": 153, + "tokens": 216 + }, + { + "name": "TestTLSPolicy_MatchesCertificateRejectsEmptyInputs", + "kind": "fn", + "startLine": 155, + "endLine": 164, + "tokens": 128 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/domain/entity/tls_policy_test.go": { + "description": "TestTLSPolicy_Effective_DANETakesPrecedenceOverMTASTS, TestTLSPolicy_Effective_FallsBackToMTASTSWithoutDANE, TestTLSPolicy_Effective_FallsBackToOpp...", + "tokens": 232, + "hash": "0fabf0b282bf323c", + "size": 809, + "mtimeMs": 1785635482524.3235, + "updatedAt": "2026-10-06T11:06:20.872Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/entity/tls_policy.go": { + "description": "TLSPolicy (35 fields); methods: Effective, RequiresValidation, CoversHost, MatchesCertificate", + "tokens": 1537, + "hash": "781d750b3bc0434a", + "size": 5379, + "mtimeMs": 1785712435284.0425, + "updatedAt": "2026-10-06T11:06:20.872Z", + "source": "scan", + "symbols": [ + { + "name": "TLSPolicy", + "kind": "class", + "startLine": 31, + "endLine": 46, + "tokens": 162 + }, + { + "name": "NewTLSPolicy", + "kind": "fn", + "startLine": 50, + "endLine": 52, + "tokens": 43 + }, + { + "name": "NewDanePolicy", + "kind": "fn", + "startLine": 57, + "endLine": 59, + "tokens": 41 + }, + { + "name": "NewMtaStsPolicy", + "kind": "fn", + "startLine": 62, + "endLine": 64, + "tokens": 44 + }, + { + "name": "Effective", + "kind": "method", + "startLine": 68, + "endLine": 77, + "tokens": 54 + }, + { + "name": "RequiresValidation", + "kind": "method", + "startLine": 81, + "endLine": 83, + "tokens": 31 + }, + { + "name": "CoversHost", + "kind": "method", + "startLine": 88, + "endLine": 98, + "tokens": 59 + }, + { + "name": "matchMxPattern", + "kind": "fn", + "startLine": 102, + "endLine": 116, + "tokens": 123 + }, + { + "name": "MatchesCertificate", + "kind": "method", + "startLine": 122, + "endLine": 134, + "tokens": 86 + }, + { + "name": "digestFor", + "kind": "fn", + "startLine": 138, + "endLine": 160, + "tokens": 189 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/domain/entity/tls_rpt_report_test.go": { + "description": "TestTlsRptReportCarriesThePolicyDomain, TestTlsRptReportParsesGzippedPayload", + "tokens": 603, + "hash": "9337eba33ccbf276", + "size": 2108, + "mtimeMs": 1787249203026.132, + "updatedAt": "2026-10-06T11:06:20.872Z", + "source": "scan", + "symbols": [ + { + "name": "TestTlsRptReportCarriesThePolicyDomain", + "kind": "fn", + "startLine": 20, + "endLine": 40, + "tokens": 206 + }, + { + "name": "TestTlsRptReportParsesGzippedPayload", + "kind": "fn", + "startLine": 43, + "endLine": 60, + "tokens": 130 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/domain/entity/tls_rpt_report.go": { + "description": "TlsRptPolicy (34 fields)", + "tokens": 718, + "hash": "c8a0485dc0bf65a7", + "size": 2511, + "mtimeMs": 1787249213083.6736, + "updatedAt": "2026-10-06T11:06:20.873Z", + "source": "scan", + "symbols": [ + { + "name": "TlsRptPolicy", + "kind": "class", + "startLine": 12, + "endLine": 16, + "tokens": 25 + }, + { + "name": "TlsRptReport", + "kind": "class", + "startLine": 18, + "endLine": 25, + "tokens": 55 + }, + { + "name": "tlsRptJsonReport", + "kind": "class", + "startLine": 27, + "endLine": 47, + "tokens": 236 + }, + { + "name": "ParseTlsRptReport", + "kind": "fn", + "startLine": 50, + "endLine": 95, + "tokens": 356 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/domain/event/events_test.go": { + "description": "TestEmailReceived_ImplementsDomainEvent, TestEmailBounced_ImplementsDomainEvent", + "tokens": 138, + "hash": "a03debbf4115c5f1", + "size": 482, + "mtimeMs": 1785635482524.9722, + "updatedAt": "2026-10-06T11:06:20.873Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/event/events.go": { + "description": "contains the domain events listed in PLAN.md section 3.", + "tokens": 1066, + "hash": "f6f5e1ffef4dab9c", + "size": 3728, + "mtimeMs": 1785635482524.3235, + "updatedAt": "2026-10-06T11:06:20.873Z", + "source": "scan", + "symbols": [ + { + "name": "DomainEvent", + "kind": "class", + "startLine": 9, + "endLine": 12, + "tokens": 20 + }, + { + "name": "EmailReceived", + "kind": "class", + "startLine": 14, + "endLine": 14, + "tokens": 17 + }, + { + "name": "Type", + "kind": "method", + "startLine": 16, + "endLine": 16, + "tokens": 21 + }, + { + "name": "AggregateID", + "kind": "method", + "startLine": 17, + "endLine": 17, + "tokens": 23 + }, + { + "name": "EmailAccepted", + "kind": "class", + "startLine": 19, + "endLine": 19, + "tokens": 17 + }, + { + "name": "Type", + "kind": "method", + "startLine": 21, + "endLine": 21, + "tokens": 21 + }, + { + "name": "AggregateID", + "kind": "method", + "startLine": 22, + "endLine": 22, + "tokens": 23 + }, + { + "name": "EmailDelivered", + "kind": "class", + "startLine": 24, + "endLine": 24, + "tokens": 16 + }, + { + "name": "Type", + "kind": "method", + "startLine": 26, + "endLine": 26, + "tokens": 22 + }, + { + "name": "AggregateID", + "kind": "method", + "startLine": 27, + "endLine": 27, + "tokens": 22 + }, + { + "name": "EmailDeferred", + "kind": "class", + "startLine": 29, + "endLine": 29, + "tokens": 16 + }, + { + "name": "Type", + "kind": "method", + "startLine": 31, + "endLine": 31, + "tokens": 21 + }, + { + "name": "AggregateID", + "kind": "method", + "startLine": 32, + "endLine": 32, + "tokens": 22 + }, + { + "name": "EmailBounced", + "kind": "class", + "startLine": 34, + "endLine": 34, + "tokens": 15 + }, + { + "name": "Type", + "kind": "method", + "startLine": 36, + "endLine": 36, + "tokens": 21 + }, + { + "name": "AggregateID", + "kind": "method", + "startLine": 37, + "endLine": 37, + "tokens": 21 + }, + { + "name": "MailboxQuotaWarning", + "kind": "class", + "startLine": 39, + "endLine": 39, + "tokens": 18 + }, + { + "name": "Type", + "kind": "method", + "startLine": 41, + "endLine": 41, + "tokens": 25 + }, + { + "name": "AggregateID", + "kind": "method", + "startLine": 42, + "endLine": 42, + "tokens": 24 + }, + { + "name": "MailboxQuotaExceeded", + "kind": "class", + "startLine": 44, + "endLine": 44, + "tokens": 19 + }, + { + "name": "Type", + "kind": "method", + "startLine": 46, + "endLine": 46, + "tokens": 25 + }, + { + "name": "AggregateID", + "kind": "method", + "startLine": 47, + "endLine": 47, + "tokens": 25 + }, + { + "name": "SpamDetected", + "kind": "class", + "startLine": 49, + "endLine": 49, + "tokens": 16 + }, + { + "name": "Type", + "kind": "method", + "startLine": 51, + "endLine": 51, + "tokens": 21 + }, + { + "name": "AggregateID", + "kind": "method", + "startLine": 52, + "endLine": 52, + "tokens": 22 + }, + { + "name": "VirusDetected", + "kind": "class", + "startLine": 54, + "endLine": 54, + "tokens": 17 + }, + { + "name": "Type", + "kind": "method", + "startLine": 56, + "endLine": 56, + "tokens": 21 + }, + { + "name": "AggregateID", + "kind": "method", + "startLine": 57, + "endLine": 57, + "tokens": 23 + }, + { + "name": "DkimRotationStarted", + "kind": "class", + "startLine": 59, + "endLine": 59, + "tokens": 18 + }, + { + "name": "Type", + "kind": "method", + "startLine": 61, + "endLine": 61, + "tokens": 25 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/domain/sieve/parser.go": { + "description": "Interface: statement (12 methods)", + "tokens": 2127, + "hash": "552355564a125db5", + "size": 7444, + "mtimeMs": 1787415740165.5603, + "updatedAt": "2026-10-06T11:06:20.873Z", + "source": "scan", + "symbols": [ + { + "name": "statement", + "kind": "class", + "startLine": 10, + "endLine": 13, + "tokens": 36 + }, + { + "name": "conditional", + "kind": "class", + "startLine": 17, + "endLine": 20, + "tokens": 20 + }, + { + "name": "branch", + "kind": "class", + "startLine": 22, + "endLine": 25, + "tokens": 15 + }, + { + "name": "stopped", + "kind": "method", + "startLine": 27, + "endLine": 34, + "tokens": 56 + }, + { + "name": "fileInto", + "kind": "class", + "startLine": 36, + "endLine": 36, + "tokens": 11 + }, + { + "name": "stopped", + "kind": "method", + "startLine": 38, + "endLine": 45, + "tokens": 77 + }, + { + "name": "discard", + "kind": "class", + "startLine": 47, + "endLine": 47, + "tokens": 6 + }, + { + "name": "stopped", + "kind": "method", + "startLine": 49, + "endLine": 52, + "tokens": 26 + }, + { + "name": "addFlag", + "kind": "class", + "startLine": 54, + "endLine": 54, + "tokens": 10 + }, + { + "name": "stopped", + "kind": "method", + "startLine": 56, + "endLine": 64, + "tokens": 57 + }, + { + "name": "keep", + "kind": "class", + "startLine": 66, + "endLine": 66, + "tokens": 6 + }, + { + "name": "stopped", + "kind": "method", + "startLine": 68, + "endLine": 68, + "tokens": 19 + }, + { + "name": "stop", + "kind": "class", + "startLine": 70, + "endLine": 70, + "tokens": 6 + }, + { + "name": "stopped", + "kind": "method", + "startLine": 72, + "endLine": 72, + "tokens": 18 + }, + { + "name": "vacation", + "kind": "class", + "startLine": 74, + "endLine": 77, + "tokens": 16 + }, + { + "name": "stopped", + "kind": "method", + "startLine": 79, + "endLine": 85, + "tokens": 54 + }, + { + "name": "splitStatements", + "kind": "fn", + "startLine": 92, + "endLine": 135, + "tokens": 233 + }, + { + "name": "parseBlock", + "kind": "fn", + "startLine": 138, + "endLine": 155, + "tokens": 98 + }, + { + "name": "parseStatement", + "kind": "fn", + "startLine": 157, + "endLine": 199, + "tokens": 342 + }, + { + "name": "parseConditional", + "kind": "fn", + "startLine": 201, + "endLine": 242, + "tokens": 270 + }, + { + "name": "readBraced", + "kind": "fn", + "startLine": 245, + "endLine": 257, + "tokens": 110 + }, + { + "name": "firstWord", + "kind": "fn", + "startLine": 259, + "endLine": 266, + "tokens": 49 + }, + { + "name": "quotedStrings", + "kind": "fn", + "startLine": 269, + "endLine": 297, + "tokens": 190 + }, + { + "name": "singleString", + "kind": "fn", + "startLine": 299, + "endLine": 305, + "tokens": 72 + }, + { + "name": "parseVacation", + "kind": "fn", + "startLine": 307, + "endLine": 318, + "tokens": 142 + } + ], + "symbolSource": "ts", + "skeleton": "type statement interface {\ntype conditional struct {\ntype branch struct {\nfunc (c conditional) stopped(msg Message, out *Outcome) bool {\ntype fileInto struct{ folder string }\nfunc (f fileInto) stopped(_ Message, out *Outcome) bool {\ntype discard struct{}\nfunc (discard) stopped(_ Message, out *Outcome) bool {\ntype addFlag struct{ flag string }\nfunc (a addFlag) stopped(_ Message, out *Outcome) bool {\ntype keep struct{}\nfunc (keep) stopped(_ Message, _ *Outcome) bool { return false }\ntype stop struct{}\nfunc (stop) stopped(_ Message, _ *Outcome) bool { return true }\ntype vacation struct {\nfunc (v vacation) stopped(msg Message, out *Outcome) bool {\nfunc splitStatements(source string) []string {\nfunc parseBlock(lines []string, i int) ([]statement, int, error) {\nfunc parseStatement(lines []string, i int) (statement, int, error) {\nfunc parseConditional(lines []string, i int) (statement, int, error) {\nfunc readBraced(lines []string, i int) ([]statement, int, error) {\nfunc firstWord(line string) string {\nfunc quotedStrings(fragment string) []string {\nfunc singleString(line, keyword string) (string, error) {\nfunc parseVacation(line string) (statement, error) {", + "importance": 0.0185 + }, + "services/protocols/internal/domain/sieve/sieve_test.go": { + "description": "TestRuleFilesTheMessageInItsFolder, TestRuleThatDoesNotMatchLeavesTheMessageAlone, TestHeaderNameIsCaseInsensitive, TestContainsIsCaseInsensitiveOn...", + "tokens": 4735, + "hash": "ee5f74dd42688051", + "size": 16570, + "mtimeMs": 1787417842590.1282, + "updatedAt": "2026-10-06T11:06:20.873Z", + "source": "scan", + "symbols": [ + { + "name": "evaluate", + "kind": "fn", + "startLine": 16, + "endLine": 23, + "tokens": 60 + }, + { + "name": "TestRuleFilesTheMessageInItsFolder", + "kind": "fn", + "startLine": 28, + "endLine": 38, + "tokens": 89 + }, + { + "name": "TestRuleThatDoesNotMatchLeavesTheMessageAlone", + "kind": "fn", + "startLine": 40, + "endLine": 47, + "tokens": 73 + }, + { + "name": "TestHeaderNameIsCaseInsensitive", + "kind": "fn", + "startLine": 51, + "endLine": 57, + "tokens": 81 + }, + { + "name": "TestContainsIsCaseInsensitiveOnTheValue", + "kind": "fn", + "startLine": 59, + "endLine": 66, + "tokens": 79 + }, + { + "name": "TestIsRequiresTheWholeValue", + "kind": "fn", + "startLine": 68, + "endLine": 76, + "tokens": 136 + }, + { + "name": "TestMatchesUsesWildcardsNotRegex", + "kind": "fn", + "startLine": 80, + "endLine": 92, + "tokens": 153 + }, + { + "name": "TestWildcardDotIsLiteral", + "kind": "fn", + "startLine": 97, + "endLine": 103, + "tokens": 86 + }, + { + "name": "TestAnyofMatchesWhenOneTestPasses", + "kind": "fn", + "startLine": 105, + "endLine": 113, + "tokens": 111 + }, + { + "name": "TestAllofRequiresEveryTest", + "kind": "fn", + "startLine": 115, + "endLine": 127, + "tokens": 174 + }, + { + "name": "TestElsifAndElse", + "kind": "fn", + "startLine": 129, + "endLine": 146, + "tokens": 165 + }, + { + "name": "TestFirstMatchingRuleDecidesTheFolder", + "kind": "fn", + "startLine": 150, + "endLine": 158, + "tokens": 117 + }, + { + "name": "TestStopEndsEvaluation", + "kind": "fn", + "startLine": 160, + "endLine": 168, + "tokens": 107 + }, + { + "name": "TestDiscardIsHonoured", + "kind": "fn", + "startLine": 170, + "endLine": 176, + "tokens": 69 + }, + { + "name": "TestAddflagMarksTheMessage", + "kind": "fn", + "startLine": 178, + "endLine": 184, + "tokens": 86 + }, + { + "name": "TestVacationProducesAReply", + "kind": "fn", + "startLine": 191, + "endLine": 205, + "tokens": 139 + }, + { + "name": "TestVacationStaysSilentForAutomatedMail", + "kind": "fn", + "startLine": 210, + "endLine": 224, + "tokens": 212 + }, + { + "name": "TestVacationDoesNotReplyToYourself", + "kind": "fn", + "startLine": 226, + "endLine": 235, + "tokens": 94 + }, + { + "name": "TestUnparseableScriptIsReportedNotFatal", + "kind": "fn", + "startLine": 242, + "endLine": 246, + "tokens": 57 + }, + { + "name": "TestEmptyScriptDoesNothing", + "kind": "fn", + "startLine": 248, + "endLine": 253, + "tokens": 71 + }, + { + "name": "TestCommentsAndBlankLinesAreIgnored", + "kind": "fn", + "startLine": 255, + "endLine": 264, + "tokens": 95 + }, + { + "name": "TestFolderNamesKeepTheirAccents", + "kind": "fn", + "startLine": 268, + "endLine": 278, + "tokens": 143 + }, + { + "name": "TestEvaluationIsBounded", + "kind": "fn", + "startLine": 281, + "endLine": 289, + "tokens": 90 + }, + { + "name": "TestMatchesIsAnchoredNotASubstring", + "kind": "fn", + "startLine": 295, + "endLine": 307, + "tokens": 146 + }, + { + "name": "TestMatchesSingleCharacterWildcard", + "kind": "fn", + "startLine": 310, + "endLine": 327, + "tokens": 194 + }, + { + "name": "TestMatchesStarInTheMiddle", + "kind": "fn", + "startLine": 330, + "endLine": 342, + "tokens": 156 + }, + { + "name": "TestMatchesDoesNotBlowUpOnManyStars", + "kind": "fn", + "startLine": 346, + "endLine": 361, + "tokens": 168 + }, + { + "name": "TestEscapedWildcardIsALiteralCharacter", + "kind": "fn", + "startLine": 370, + "endLine": 384, + "tokens": 179 + }, + { + "name": "TestEscapedQuestionMarkIsALiteralCharacter", + "kind": "fn", + "startLine": 386, + "endLine": 398, + "tokens": 151 + }, + { + "name": "TestVacationStaysSilentForReservedAddressesWithSuffixes", + "kind": "fn", + "startLine": 407, + "endLine": 425, + "tokens": 158 + } + ], + "symbolSource": "ts", + "skeleton": "func evaluate(t *testing.T, script string, msg Message) Outcome {\nfunc TestRuleFilesTheMessageInItsFolder(t *testing.T) {\nfunc TestRuleThatDoesNotMatchLeavesTheMessageAlone(t *testing.T) {\nfunc TestHeaderNameIsCaseInsensitive(t *testing.T) {\nfunc TestContainsIsCaseInsensitiveOnTheValue(t *testing.T) {\nfunc TestIsRequiresTheWholeValue(t *testing.T) {\nfunc TestMatchesUsesWildcardsNotRegex(t *testing.T) {\nfunc TestWildcardDotIsLiteral(t *testing.T) {\nfunc TestAnyofMatchesWhenOneTestPasses(t *testing.T) {\nfunc TestAllofRequiresEveryTest(t *testing.T) {\nfunc TestElsifAndElse(t *testing.T) {\nfunc TestFirstMatchingRuleDecidesTheFolder(t *testing.T) {\nfunc TestStopEndsEvaluation(t *testing.T) {\nfunc TestDiscardIsHonoured(t *testing.T) {\nfunc TestAddflagMarksTheMessage(t *testing.T) {\nfunc TestVacationProducesAReply(t *testing.T) {\nfunc TestVacationStaysSilentForAutomatedMail(t *testing.T) {\nfunc TestVacationDoesNotReplyToYourself(t *testing.T) {\nfunc TestUnparseableScriptIsReportedNotFatal(t *testing.T) {\nfunc TestEmptyScriptDoesNothing(t *testing.T) {\nfunc TestCommentsAndBlankLinesAreIgnored(t *testing.T) {\nfunc TestFolderNamesKeepTheirAccents(t *testing.T) {\nfunc TestEvaluationIsBounded(t *testing.T) {\nfunc TestMatchesIsAnchoredNotASubstring(t *testing.T) {\nfunc TestMatchesSingleCharacterWildcard(t *testing.T) {\nfunc TestMatchesStarInTheMiddle(t *testing.T) {", + "importance": 0.0144 + }, + "services/protocols/internal/domain/sieve/sieve.go": { + "description": "evaluates the mail-filtering rules a mailbox owner writes.", + "tokens": 926, + "hash": "a5a9084bb94fb006", + "size": 3240, + "mtimeMs": 1787415704847.2263, + "updatedAt": "2026-10-06T11:06:20.873Z", + "source": "scan", + "symbols": [ + { + "name": "Message", + "kind": "class", + "startLine": 24, + "endLine": 33, + "tokens": 107 + }, + { + "name": "header", + "kind": "method", + "startLine": 35, + "endLine": 40, + "tokens": 43 + }, + { + "name": "VacationReply", + "kind": "class", + "startLine": 43, + "endLine": 46, + "tokens": 18 + }, + { + "name": "Outcome", + "kind": "class", + "startLine": 49, + "endLine": 60, + "tokens": 113 + }, + { + "name": "Script", + "kind": "class", + "startLine": 63, + "endLine": 65, + "tokens": 14 + }, + { + "name": "Parse", + "kind": "fn", + "startLine": 71, + "endLine": 84, + "tokens": 126 + }, + { + "name": "Evaluate", + "kind": "fn", + "startLine": 87, + "endLine": 94, + "tokens": 45 + }, + { + "name": "run", + "kind": "fn", + "startLine": 97, + "endLine": 104, + "tokens": 48 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/domain/sieve/test.go": { + "description": "Interface: test (6 methods)", + "tokens": 1741, + "hash": "5bdbc1c9be981a5d", + "size": 6091, + "mtimeMs": 1787416817545.6025, + "updatedAt": "2026-10-06T11:06:20.874Z", + "source": "scan", + "symbols": [ + { + "name": "test", + "kind": "class", + "startLine": 9, + "endLine": 11, + "tokens": 15 + }, + { + "name": "headerTest", + "kind": "class", + "startLine": 14, + "endLine": 18, + "tokens": 24 + }, + { + "name": "matches", + "kind": "method", + "startLine": 20, + "endLine": 27, + "tokens": 51 + }, + { + "name": "anyOf", + "kind": "class", + "startLine": 29, + "endLine": 29, + "tokens": 10 + }, + { + "name": "matches", + "kind": "method", + "startLine": 31, + "endLine": 38, + "tokens": 38 + }, + { + "name": "allOf", + "kind": "class", + "startLine": 40, + "endLine": 40, + "tokens": 10 + }, + { + "name": "matches", + "kind": "method", + "startLine": 42, + "endLine": 49, + "tokens": 42 + }, + { + "name": "notTest", + "kind": "class", + "startLine": 51, + "endLine": 51, + "tokens": 10 + }, + { + "name": "matches", + "kind": "method", + "startLine": 53, + "endLine": 53, + "tokens": 22 + }, + { + "name": "compare", + "kind": "fn", + "startLine": 60, + "endLine": 71, + "tokens": 81 + }, + { + "name": "wildcardMatch", + "kind": "fn", + "startLine": 81, + "endLine": 135, + "tokens": 378 + }, + { + "name": "parseTest", + "kind": "fn", + "startLine": 138, + "endLine": 170, + "tokens": 294 + }, + { + "name": "parseTestList", + "kind": "fn", + "startLine": 174, + "endLine": 225, + "tokens": 332 + }, + { + "name": "parseHeaderTest", + "kind": "fn", + "startLine": 227, + "endLine": 240, + "tokens": 133 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/domain/sieve/vacation.go": { + "description": "shouldAutoReply decides whether a message may be answered automatically.", + "tokens": 701, + "hash": "e32134baf71da9b6", + "size": 2451, + "mtimeMs": 1787417356584.9895, + "updatedAt": "2026-10-06T11:06:20.874Z", + "source": "scan", + "symbols": [ + { + "name": "shouldAutoReply", + "kind": "fn", + "startLine": 11, + "endLine": 75, + "tokens": 579 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go": { + "description": "TestBuildThunderbirdAutoconfigXML, TestBuildOutlookAutodiscoverXML", + "tokens": 220, + "hash": "861f5b7c3e48f600", + "size": 769, + "mtimeMs": 1785635482525.0513, + "updatedAt": "2026-10-06T11:06:20.874Z", + "source": "scan", + "importance": 0.1886 + }, + "services/protocols/internal/domain/valueobject/autoconfig_xml.go": { + "description": "BuildThunderbirdAutoconfigXML, BuildOutlookAutodiscoverXML", + "tokens": 735, + "hash": "1bc752bf0b8898be", + "size": 2570, + "mtimeMs": 1785635482524.9722, + "updatedAt": "2026-10-06T11:06:20.874Z", + "source": "scan", + "symbols": [ + { + "name": "BuildThunderbirdAutoconfigXML", + "kind": "fn", + "startLine": 8, + "endLine": 42, + "tokens": 352 + }, + { + "name": "BuildOutlookAutodiscoverXML", + "kind": "fn", + "startLine": 45, + "endLine": 79, + "tokens": 322 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/delivery_status_code_test.go": { + "description": "TestNewDeliveryStatusCode_AcceptsSuccess, TestNewDeliveryStatusCode_AcceptsPermanentFailure, TestNewDeliveryStatusCode_RejectsInvalidClass", + "tokens": 220, + "hash": "1a8674d9d1b68303", + "size": 770, + "mtimeMs": 1785635482525.0513, + "updatedAt": "2026-10-06T11:06:20.874Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/delivery_status_code.go": { + "description": "DeliveryStatusCode (6 fields); methods: String", + "tokens": 206, + "hash": "b3eca24b6fc27545", + "size": 719, + "mtimeMs": 1785635482525.0513, + "updatedAt": "2026-10-06T11:06:20.874Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/domain_name_test.go": { + "description": "TestNewDomainName_AcceptsValidFQDN, TestNewDomainName_RejectsEmpty, TestNewDomainName_RejectsLabelWithoutDot, TestNewDomainName_RejectsTrailingHyph...", + "tokens": 398, + "hash": "19500d191cbf90fd", + "size": 1393, + "mtimeMs": 1785635482525.0513, + "updatedAt": "2026-10-06T11:06:20.874Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/domain_name.go": { + "description": "DomainName (13 fields); methods: String, IsPunycode", + "tokens": 403, + "hash": "cfd7bd03cc027d61", + "size": 1410, + "mtimeMs": 1785635482525.0513, + "updatedAt": "2026-10-06T11:06:20.874Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/dsn_bounce_test.go": { + "description": "TestBuildDsnReport_PreventsBounceLoopForEmptySender, TestBuildDsnReport_GeneratesValidMIME, TestBuildDsnReport_UsesTheConfiguredMailHost, TestBuild...", + "tokens": 721, + "hash": "556fb9f0d361d5be", + "size": 2523, + "mtimeMs": 1785711428684.797, + "updatedAt": "2026-10-06T11:06:20.875Z", + "source": "scan", + "symbols": [ + { + "name": "TestBuildDsnReport_PreventsBounceLoopForEmptySender", + "kind": "fn", + "startLine": 8, + "endLine": 18, + "tokens": 132 + }, + { + "name": "TestBuildDsnReport_GeneratesValidMIME", + "kind": "fn", + "startLine": 20, + "endLine": 36, + "tokens": 197 + }, + { + "name": "TestBuildDsnReport_UsesTheConfiguredMailHost", + "kind": "fn", + "startLine": 41, + "endLine": 54, + "tokens": 190 + }, + { + "name": "TestBuildDsnReport_MarksItselfAutomatic", + "kind": "fn", + "startLine": 58, + "endLine": 64, + "tokens": 100 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/dsn_bounce.go": { + "description": "BuildDsnReport", + "tokens": 1106, + "hash": "dc04ce8ceefd6b44", + "size": 3870, + "mtimeMs": 1785711428684.676, + "updatedAt": "2026-10-06T11:06:20.875Z", + "source": "scan", + "symbols": [ + { + "name": "DsnAction", + "kind": "class", + "startLine": 9, + "endLine": 9, + "tokens": 6 + }, + { + "name": "BuildDsnReport", + "kind": "fn", + "startLine": 31, + "endLine": 92, + "tokens": 841 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/email_address_test.go": { + "description": "TestNewEmailAddress_AcceptsValidAddress, TestNewEmailAddress_RejectsMissingAtSign, TestNewEmailAddress_RejectsEmptyLocalPart, TestNewEmailAddress_R...", + "tokens": 411, + "hash": "3010789c30f205d7", + "size": 1438, + "mtimeMs": 1785635482525.0513, + "updatedAt": "2026-10-06T11:06:20.875Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/email_address.go": { + "description": "contains the domain's immutable, self-validating value objects.", + "tokens": 430, + "hash": "aee4786bf3e23940", + "size": 1505, + "mtimeMs": 1785635482525.0513, + "updatedAt": "2026-10-06T11:06:20.875Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/message_id_test.go": { + "description": "TestNewMessageID_AcceptsAngleBracketForm, TestNewMessageID_RejectsEmpty, TestNewMessageID_RejectsMissingAngleBrackets", + "tokens": 202, + "hash": "282c7e48abf811de", + "size": 707, + "mtimeMs": 1785635482525.0513, + "updatedAt": "2026-10-06T11:06:20.875Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/message_id.go": { + "description": "MessageID (6 fields); methods: String", + "tokens": 186, + "hash": "6b6c8746878f2a86", + "size": 651, + "mtimeMs": 1785635482525.0513, + "updatedAt": "2026-10-06T11:06:20.875Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/mta_sts_policy_test.go": { + "description": "TestMtaStsPolicy_Format", + "tokens": 178, + "hash": "ecddbbfb2cd784ed", + "size": 621, + "mtimeMs": 1785635482525.0513, + "updatedAt": "2026-10-06T11:06:20.875Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/mta_sts_policy.go": { + "description": "MtaStsPolicy (14 fields); methods: Format", + "tokens": 260, + "hash": "bbab7081b32f5c4b", + "size": 910, + "mtimeMs": 1785635482525.0513, + "updatedAt": "2026-10-06T11:06:20.875Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/password_hash_test.go": { + "description": "TestNewPasswordHash_AcceptsArgon2idPHCString, TestNewPasswordHash_RejectsNonArgon2idScheme, TestNewPasswordHash_RejectsEmpty", + "tokens": 208, + "hash": "3136989b893d378f", + "size": 725, + "mtimeMs": 1785635482525.0513, + "updatedAt": "2026-10-06T11:06:20.875Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/password_hash.go": { + "description": "PasswordHash (5 fields); methods: String", + "tokens": 223, + "hash": "3b74d2f7fae2dfd3", + "size": 778, + "mtimeMs": 1785635482525.0513, + "updatedAt": "2026-10-06T11:06:20.875Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/protocol_security_level_test.go": { + "description": "TestProtocolSecurityLevel_StringRoundTrip", + "tokens": 120, + "hash": "a995d84b524bfd31", + "size": 420, + "mtimeMs": 1785635482525.0513, + "updatedAt": "2026-10-06T11:06:20.876Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/protocol_security_level.go": { + "description": "ProtocolSecurityLevel is the effective TLS requirement for a listener (see PLAN.md section 4).", + "tokens": 190, + "hash": "c70e4927232879e1", + "size": 664, + "mtimeMs": 1785635482525.0513, + "updatedAt": "2026-10-06T11:06:20.876Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/quota_limit_test.go": { + "description": "TestNewQuotaLimit_AcceptsPositiveBytes, TestNewQuotaLimit_RejectsZero, TestNewQuotaLimit_RejectsNegative, TestQuotaLimit_Exceeds", + "tokens": 261, + "hash": "527683c9d564a051", + "size": 912, + "mtimeMs": 1785635482525.0513, + "updatedAt": "2026-10-06T11:06:20.876Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/quota_limit.go": { + "description": "QuotaLimit is a strictly positive byte quota (see PLAN.md section 3, Mailbox invariant).", + "tokens": 187, + "hash": "2836bf53dca581c6", + "size": 652, + "mtimeMs": 1785635482525.0513, + "updatedAt": "2026-10-06T11:06:20.876Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/scan_result_test.go": { + "description": "TestNewCleanScanResult, TestNewVirusScanResult", + "tokens": 130, + "hash": "f1805c18577692f4", + "size": 453, + "mtimeMs": 1785635482525.0513, + "updatedAt": "2026-10-06T11:06:20.876Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/scan_result.go": { + "description": "ScanResult (16 fields)", + "tokens": 268, + "hash": "f3cbe61c2a7d6e85", + "size": 935, + "mtimeMs": 1785635482525.0513, + "updatedAt": "2026-10-06T11:06:20.876Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/spam_verdict_test.go": { + "description": "TestSpamVerdictFromScore_MatchesThresholds", + "tokens": 164, + "hash": "d39324dfa97614f0", + "size": 571, + "mtimeMs": 1785635482525.0513, + "updatedAt": "2026-10-06T11:06:20.876Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/spam_verdict.go": { + "description": "SpamVerdict classifies a message by Rspamd score (thresholds from PLAN.md section 10.1).", + "tokens": 136, + "hash": "ace33a2e1057a62d", + "size": 474, + "mtimeMs": 1785635482525.0513, + "updatedAt": "2026-10-06T11:06:20.876Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/tlsa_record_test.go": { + "description": "TestNewTLSARecord_AcceptsUsage3Selector1MatchingType1, TestNewTLSARecord_RejectsUsage2, TestNewTLSARecord_RejectsWrongLengthHash", + "tokens": 328, + "hash": "cfebaa39124cbdd3", + "size": 1147, + "mtimeMs": 1785635482525.0513, + "updatedAt": "2026-10-06T11:06:20.876Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/domain/valueobject/tlsa_record.go": { + "description": "TLSARecord models a DANE TLSA RR (RFC 6698/7671). Usage 2 (PKIX-CA) is rejected", + "tokens": 344, + "hash": "03165a8a0f74f032", + "size": 1202, + "mtimeMs": 1785635482525.0513, + "updatedAt": "2026-10-06T11:06:20.876Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/e2e/antispam_antivirus_e2e_test.go": { + "description": "TestAntispamAntivirusEndToEnd", + "tokens": 1692, + "hash": "609f0fb7b81deaa9", + "size": 5921, + "mtimeMs": 1787399704631.07, + "updatedAt": "2026-10-06T11:06:20.876Z", + "source": "scan", + "symbols": [ + { + "name": "TestAntispamAntivirusEndToEnd", + "kind": "fn", + "startLine": 25, + "endLine": 163, + "tokens": 1284 + }, + { + "name": "sendTestSMTPMessage", + "kind": "fn", + "startLine": 165, + "endLine": 197, + "tokens": 206 + }, + { + "name": "fmtError", + "kind": "fn", + "startLine": 199, + "endLine": 201, + "tokens": 26 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go", + "services/protocols/internal/application/usecase/apply_rules_test.go", + "services/protocols/internal/infrastructure/clamav/clamav_adapter_test.go", + "services/protocols/internal/infrastructure/diskstorage/local_disk_blob_reader_test.go", + "services/protocols/internal/infrastructure/postgres/acme_repository.go", + "config/rspamd/actions.conf", + "services/protocols/internal/presentation/smtp/backend_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/e2e/dkim_rotation_e2e_test.go": { + "description": "TestDkimRotationLeavesAUsableKey", + "tokens": 1352, + "hash": "193afe49e70cf209", + "size": 4731, + "mtimeMs": 1787399704631.2756, + "updatedAt": "2026-10-06T11:06:20.876Z", + "source": "scan", + "symbols": [ + { + "name": "TestDkimRotationLeavesAUsableKey", + "kind": "fn", + "startLine": 25, + "endLine": 142, + "tokens": 1142 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go", + "services/protocols/internal/infrastructure/dkim/keygen.go", + "services/protocols/internal/infrastructure/postgres/acme_repository.go", + "services/protocols/internal/infrastructure/vault/secret_vault_test.go", + "services/protocols/internal/domain/bimi/svg_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/e2e/dns_sync_e2e_test.go": { + "description": "e2eFakeDnsProvider (21 fields); methods: GetZoneID, ListRecords, CreateRecord, UpdateRecord", + "tokens": 1052, + "hash": "932dd9d1d532003f", + "size": 3682, + "mtimeMs": 1787399704631.413, + "updatedAt": "2026-10-06T11:06:20.876Z", + "source": "scan", + "symbols": [ + { + "name": "TestDnsSyncAndSystemAliasesEndToEnd", + "kind": "fn", + "startLine": 16, + "endLine": 85, + "tokens": 714 + }, + { + "name": "e2eFakeDnsProvider", + "kind": "class", + "startLine": 87, + "endLine": 89, + "tokens": 21 + }, + { + "name": "GetZoneID", + "kind": "method", + "startLine": 91, + "endLine": 93, + "tokens": 35 + }, + { + "name": "ListRecords", + "kind": "method", + "startLine": 95, + "endLine": 101, + "tokens": 58 + }, + { + "name": "CreateRecord", + "kind": "method", + "startLine": 103, + "endLine": 107, + "tokens": 51 + }, + { + "name": "UpdateRecord", + "kind": "method", + "startLine": 109, + "endLine": 113, + "tokens": 51 + }, + { + "name": "DeleteRecord", + "kind": "method", + "startLine": 115, + "endLine": 117, + "tokens": 32 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/usecase/apply_rules_test.go", + "services/protocols/internal/domain/entity/dkim_key_pair_test.go", + "services/protocols/internal/infrastructure/postgres/acme_repository.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/e2e/events_ws_e2e_test.go": { + "description": "TestEventStreamPushesDeliveredMail, TestEventStreamRequiresUserSession", + "tokens": 1752, + "hash": "c097f1ce2a90ba96", + "size": 6132, + "mtimeMs": 1787399704631.4895, + "updatedAt": "2026-10-06T11:06:20.877Z", + "source": "scan", + "symbols": [ + { + "name": "TestEventStreamPushesDeliveredMail", + "kind": "fn", + "startLine": 26, + "endLine": 150, + "tokens": 1241 + }, + { + "name": "TestEventStreamRequiresUserSession", + "kind": "fn", + "startLine": 153, + "endLine": 181, + "tokens": 276 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go", + "services/protocols/internal/application/usecase/apply_rules_test.go", + "services/protocols/internal/infrastructure/diskstorage/local_disk_blob_reader_test.go", + "services/protocols/internal/infrastructure/postgres/acme_repository.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/e2e/f2_complete_e2e_test.go": { + "description": "TestF2CompleteSuiteEndToEnd", + "tokens": 4535, + "hash": "4e8c7ee521af4a85", + "size": 15870, + "mtimeMs": 1787399704632.2104, + "updatedAt": "2026-10-06T11:06:20.877Z", + "source": "scan", + "symbols": [ + { + "name": "TestF2CompleteSuiteEndToEnd", + "kind": "fn", + "startLine": 29, + "endLine": 454, + "tokens": 4309 + } + ], + "symbolSource": "ts", + "skeleton": "func TestF2CompleteSuiteEndToEnd(t *testing.T) {", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/application/usecase/apply_rules_test.go", + "services/protocols/internal/infrastructure/diskstorage/local_disk_blob_reader_test.go", + "services/protocols/internal/infrastructure/postgres/acme_repository.go", + "services/protocols/internal/presentation/imap/backend_test.go", + "services/protocols/internal/presentation/managesieve/conn_test.go", + "services/protocols/internal/presentation/pop3/conn_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/e2e/f3_complete_e2e_test.go": { + "description": "fakeDnsProvider (56 fields); methods: GetZoneID, ListRecords, CreateRecord, UpdateRecord", + "tokens": 4487, + "hash": "bc0717949074d8b2", + "size": 15704, + "mtimeMs": 1787399704632.6245, + "updatedAt": "2026-10-06T11:06:20.877Z", + "source": "scan", + "symbols": [ + { + "name": "fakeDnsProvider", + "kind": "class", + "startLine": 31, + "endLine": 33, + "tokens": 20 + }, + { + "name": "newFakeDnsProvider", + "kind": "fn", + "startLine": 35, + "endLine": 37, + "tokens": 33 + }, + { + "name": "GetZoneID", + "kind": "method", + "startLine": 39, + "endLine": 41, + "tokens": 41 + }, + { + "name": "ListRecords", + "kind": "method", + "startLine": 43, + "endLine": 49, + "tokens": 58 + }, + { + "name": "CreateRecord", + "kind": "method", + "startLine": 51, + "endLine": 56, + "tokens": 77 + }, + { + "name": "UpdateRecord", + "kind": "method", + "startLine": 58, + "endLine": 62, + "tokens": 66 + }, + { + "name": "DeleteRecord", + "kind": "method", + "startLine": 64, + "endLine": 72, + "tokens": 58 + }, + { + "name": "TestPhase3CompleteEndToEnd", + "kind": "fn", + "startLine": 74, + "endLine": 408, + "tokens": 3592 + }, + { + "name": "sendTestEmail", + "kind": "fn", + "startLine": 410, + "endLine": 454, + "tokens": 315 + } + ], + "symbolSource": "ts", + "skeleton": "type fakeDnsProvider struct {\nfunc newFakeDnsProvider() *fakeDnsProvider {\nfunc (f *fakeDnsProvider) GetZoneID(_ context.Context, domainName string) (string, error) {\nfunc (f *fakeDnsProvider) ListRecords(_ context.Context, _ string) ([]entity.DnsRecord, error) {\nfunc (f *fakeDnsProvider) CreateRecord(_ context.Context, _ string, record entity.DnsRecord) error {\nfunc (f *fakeDnsProvider) UpdateRecord(_ context.Context, _ string, record entity.DnsRecord) error {\nfunc (f *fakeDnsProvider) DeleteRecord(_ context.Context, _ string, recordID string) error {\nfunc TestPhase3CompleteEndToEnd(t *testing.T) {\nfunc sendTestEmail(addr string, from string, to string, msg string) error {", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go", + "services/protocols/internal/application/usecase/apply_rules_test.go", + "services/protocols/internal/domain/entity/dkim_key_pair_test.go", + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go", + "services/protocols/internal/infrastructure/clamav/clamav_adapter_test.go", + "services/protocols/internal/infrastructure/diskstorage/local_disk_blob_reader_test.go", + "services/protocols/internal/infrastructure/postgres/acme_repository.go", + "config/rspamd/actions.conf", + "services/protocols/internal/presentation/smtp/backend_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/e2e/http_services_e2e_test.go": { + "description": "TestHttpServicesEndToEnd", + "tokens": 1425, + "hash": "abe00a61d3734a3a", + "size": 4986, + "mtimeMs": 1787399704632.9004, + "updatedAt": "2026-10-06T11:06:20.877Z", + "source": "scan", + "symbols": [ + { + "name": "TestHttpServicesEndToEnd", + "kind": "fn", + "startLine": 20, + "endLine": 128, + "tokens": 1320 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go", + "services/protocols/internal/application/usecase/apply_rules_test.go", + "services/protocols/internal/infrastructure/postgres/acme_repository.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/e2e/imap_core_e2e_test.go": { + "description": "TestImapCoreEndToEnd", + "tokens": 3244, + "hash": "50985428fc16a352", + "size": 11354, + "mtimeMs": 1787399725310.788, + "updatedAt": "2026-10-06T11:06:20.877Z", + "source": "scan", + "symbols": [ + { + "name": "TestImapCoreEndToEnd", + "kind": "fn", + "startLine": 25, + "endLine": 299, + "tokens": 3068 + } + ], + "symbolSource": "ts", + "skeleton": "func TestImapCoreEndToEnd(t *testing.T) {", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/application/usecase/apply_rules_test.go", + "services/protocols/internal/infrastructure/diskstorage/local_disk_blob_reader_test.go", + "services/protocols/internal/infrastructure/postgres/acme_repository.go", + "services/protocols/internal/presentation/imap/backend_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/e2e/imap_idle_condstore_e2e_test.go": { + "description": "TestImapIdleAndCondStoreEndToEnd", + "tokens": 2089, + "hash": "d079bd5fe8605b6b", + "size": 7309, + "mtimeMs": 1787399704633.3662, + "updatedAt": "2026-10-06T11:06:20.877Z", + "source": "scan", + "symbols": [ + { + "name": "TestImapIdleAndCondStoreEndToEnd", + "kind": "fn", + "startLine": 25, + "endLine": 213, + "tokens": 1912 + } + ], + "symbolSource": "ts", + "skeleton": "func TestImapIdleAndCondStoreEndToEnd(t *testing.T) {", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/application/usecase/apply_rules_test.go", + "services/protocols/internal/infrastructure/diskstorage/local_disk_blob_reader_test.go", + "services/protocols/internal/infrastructure/postgres/acme_repository.go", + "services/protocols/internal/presentation/imap/backend_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/e2e/managesieve_e2e_test.go": { + "description": "TestManageSieveEndToEnd", + "tokens": 1814, + "hash": "e947dd4ccfd6152d", + "size": 6348, + "mtimeMs": 1787399704637.3716, + "updatedAt": "2026-10-06T11:06:20.878Z", + "source": "scan", + "symbols": [ + { + "name": "TestManageSieveEndToEnd", + "kind": "fn", + "startLine": 23, + "endLine": 216, + "tokens": 1690 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/usecase/apply_rules_test.go", + "services/protocols/internal/infrastructure/postgres/acme_repository.go", + "services/protocols/internal/presentation/managesieve/conn_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/e2e/migrations_test.go": { + "description": "", + "tokens": 595, + "hash": "f879203fbed058b6", + "size": 2082, + "mtimeMs": 1785635482525.9329, + "updatedAt": "2026-10-06T11:06:20.878Z", + "source": "scan", + "symbols": [ + { + "name": "applyMigrations", + "kind": "fn", + "startLine": 26, + "endLine": 55, + "tokens": 214 + }, + { + "name": "selfSignedTLSCert", + "kind": "fn", + "startLine": 59, + "endLine": 79, + "tokens": 192 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/infrastructure/tls/acme_cert_watcher_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/e2e/outbound_queue_e2e_test.go": { + "description": "staticMXResolver (27 fields); methods: LookupMX", + "tokens": 1791, + "hash": "deeac8cc12775b3e", + "size": 6267, + "mtimeMs": 1787399704637.6614, + "updatedAt": "2026-10-06T11:06:20.878Z", + "source": "scan", + "symbols": [ + { + "name": "staticMXResolver", + "kind": "class", + "startLine": 20, + "endLine": 22, + "tokens": 13 + }, + { + "name": "LookupMX", + "kind": "method", + "startLine": 24, + "endLine": 26, + "tokens": 34 + }, + { + "name": "TestOutboundQueueEndToEnd", + "kind": "fn", + "startLine": 28, + "endLine": 155, + "tokens": 1328 + }, + { + "name": "handleMockSMTPSession", + "kind": "fn", + "startLine": 157, + "endLine": 191, + "tokens": 300 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/usecase/apply_rules_test.go", + "services/protocols/internal/domain/entity/dkim_key_pair_test.go", + "services/protocols/internal/infrastructure/diskstorage/local_disk_blob_reader_test.go", + "services/protocols/internal/infrastructure/postgres/acme_repository.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/e2e/pop3_e2e_test.go": { + "description": "TestPop3EndToEnd", + "tokens": 1926, + "hash": "8adf794e03f6a5b5", + "size": 6740, + "mtimeMs": 1787399704637.9463, + "updatedAt": "2026-10-06T11:06:20.878Z", + "source": "scan", + "symbols": [ + { + "name": "TestPop3EndToEnd", + "kind": "fn", + "startLine": 24, + "endLine": 239, + "tokens": 1779 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/application/usecase/apply_rules_test.go", + "services/protocols/internal/infrastructure/diskstorage/local_disk_blob_reader_test.go", + "services/protocols/internal/infrastructure/postgres/acme_repository.go", + "services/protocols/internal/presentation/pop3/conn_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/e2e/ports_test.go": { + "description": "TestEmbeddedPostgresPortsAreOutsideTheEphemeralRange, TestEmbeddedPostgresPortsAreUnique, TestEachTestConnectsToThePortItStarted", + "tokens": 989, + "hash": "4b0a5f357bcc914a", + "size": 3459, + "mtimeMs": 1787399690290.3398, + "updatedAt": "2026-10-06T11:06:20.878Z", + "source": "scan", + "symbols": [ + { + "name": "TestEmbeddedPostgresPortsAreOutsideTheEphemeralRange", + "kind": "fn", + "startLine": 33, + "endLine": 64, + "tokens": 268 + }, + { + "name": "TestEmbeddedPostgresPortsAreUnique", + "kind": "fn", + "startLine": 67, + "endLine": 84, + "tokens": 149 + }, + { + "name": "TestEachTestConnectsToThePortItStarted", + "kind": "fn", + "startLine": 88, + "endLine": 107, + "tokens": 169 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/e2e/smtp_inbound_e2e_test.go": { + "description": "contains end-to-end validation of the F0/F1 scope declared in", + "tokens": 2808, + "hash": "f684c48336b25734", + "size": 9828, + "mtimeMs": 1787399704638.441, + "updatedAt": "2026-10-06T11:06:20.878Z", + "source": "scan", + "symbols": [ + { + "name": "TestInboundSMTPEndToEnd", + "kind": "fn", + "startLine": 37, + "endLine": 264, + "tokens": 2261 + }, + { + "name": "repoRoot", + "kind": "fn", + "startLine": 267, + "endLine": 289, + "tokens": 233 + } + ], + "symbolSource": "ts", + "skeleton": "func TestInboundSMTPEndToEnd(t *testing.T) {\nfunc repoRoot(t *testing.T) string {", + "imports": [ + "services/protocols/internal/presentation/smtp/backend_test.go", + "services/protocols/internal/application/usecase/apply_rules_test.go", + "services/protocols/internal/infrastructure/diskstorage/local_disk_blob_reader_test.go", + "services/protocols/internal/infrastructure/postgres/acme_repository.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/e2e/submission_e2e_test.go": { + "description": "plainAuthClient (68 fields); methods: Start, Next", + "tokens": 3375, + "hash": "feab0cec2fb8ab5a", + "size": 11810, + "mtimeMs": 1787399704638.6812, + "updatedAt": "2026-10-06T11:06:20.878Z", + "source": "scan", + "symbols": [ + { + "name": "TestSubmissionEndToEnd", + "kind": "fn", + "startLine": 32, + "endLine": 196, + "tokens": 1829 + }, + { + "name": "submitMessage", + "kind": "fn", + "startLine": 199, + "endLine": 230, + "tokens": 278 + }, + { + "name": "assertSubmissionRefused", + "kind": "fn", + "startLine": 234, + "endLine": 260, + "tokens": 224 + }, + { + "name": "assertSenderRefused", + "kind": "fn", + "startLine": 262, + "endLine": 276, + "tokens": 128 + }, + { + "name": "startCapturingMX", + "kind": "fn", + "startLine": 279, + "endLine": 338, + "tokens": 388 + }, + { + "name": "plainAuthClient", + "kind": "class", + "startLine": 341, + "endLine": 344, + "tokens": 24 + }, + { + "name": "plainAuth", + "kind": "fn", + "startLine": 346, + "endLine": 348, + "tokens": 37 + }, + { + "name": "Start", + "kind": "method", + "startLine": 350, + "endLine": 352, + "tokens": 42 + }, + { + "name": "Next", + "kind": "method", + "startLine": 354, + "endLine": 360, + "tokens": 62 + } + ], + "symbolSource": "ts", + "skeleton": "func TestSubmissionEndToEnd(t *testing.T) {\nfunc submitMessage(t *testing.T, addr, username, password, recipient string) {\nfunc assertSubmissionRefused(t *testing.T, addr, username, password string, recipients []string) {\nfunc assertSenderRefused(t *testing.T, addr, username, password, forgedSender string) {\nfunc startCapturingMX(t *testing.T, received chan<- string) string {\ntype plainAuthClient struct {\nfunc plainAuth(username, password string) *plainAuthClient {\nfunc (a *plainAuthClient) Start() (mech string, ir []byte, err error) {\nfunc (a *plainAuthClient) Next(challenge []byte) ([]byte, error) {", + "imports": [ + "services/protocols/internal/infrastructure/tls/acme_cert_watcher_test.go", + "services/protocols/internal/application/usecase/apply_rules_test.go", + "services/protocols/internal/infrastructure/diskstorage/local_disk_blob_reader_test.go", + "services/protocols/internal/infrastructure/dkim/keygen.go", + "services/protocols/internal/infrastructure/postgres/acme_repository.go", + "services/protocols/internal/infrastructure/vault/secret_vault_test.go", + "services/protocols/internal/presentation/smtp/backend_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/e2e/webmail_api_e2e_test.go": { + "description": "TestWebmailApiEndToEnd, TestWebmailApiRejectsCrossMailboxAccess, TestWebmailApiRequiresUserSession", + "tokens": 3572, + "hash": "00ac28be482b5ac7", + "size": 12499, + "mtimeMs": 1787399704638.8528, + "updatedAt": "2026-10-06T11:06:20.879Z", + "source": "scan", + "symbols": [ + { + "name": "mintSession", + "kind": "fn", + "startLine": 30, + "endLine": 51, + "tokens": 227 + }, + { + "name": "TestWebmailApiEndToEnd", + "kind": "fn", + "startLine": 53, + "endLine": 250, + "tokens": 2006 + }, + { + "name": "TestWebmailApiRejectsCrossMailboxAccess", + "kind": "fn", + "startLine": 253, + "endLine": 330, + "tokens": 818 + }, + { + "name": "TestWebmailApiRequiresUserSession", + "kind": "fn", + "startLine": 333, + "endLine": 359, + "tokens": 269 + } + ], + "symbolSource": "ts", + "skeleton": "func mintSession(t *testing.T, email, surface string, ttl time.Duration) string {\nfunc TestWebmailApiEndToEnd(t *testing.T) {\nfunc TestWebmailApiRejectsCrossMailboxAccess(t *testing.T) {\nfunc TestWebmailApiRequiresUserSession(t *testing.T) {", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go", + "services/protocols/internal/application/usecase/apply_rules_test.go", + "services/protocols/internal/infrastructure/diskstorage/local_disk_blob_reader_test.go", + "services/protocols/internal/infrastructure/postgres/acme_repository.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/health/health_test.go": { + "description": "TestHandler_Live_AlwaysReturns200, TestHandler_Ready_Returns200WhenPingSucceeds, TestHandler_Ready_Returns503WhenPingFails", + "tokens": 334, + "hash": "de8bbd61fe45a75f", + "size": 1168, + "mtimeMs": 1785635482526.4373, + "updatedAt": "2026-10-06T11:06:20.879Z", + "source": "scan", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/health/health.go": { + "description": "exposes liveness/readiness endpoints (PLAN.md section 11.5).", + "tokens": 205, + "hash": "5e522d2fe28653f2", + "size": 715, + "mtimeMs": 1785635482525.9329, + "updatedAt": "2026-10-06T11:06:20.879Z", + "source": "scan", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/acme/issuer.go": { + "description": "implements mode B of PLAN.md section 8.3: LambdaMail obtains", + "tokens": 2133, + "hash": "b9a70b061db10685", + "size": 7464, + "mtimeMs": 1785635482526.4897, + "updatedAt": "2026-10-06T11:06:20.880Z", + "source": "scan", + "symbols": [ + { + "name": "Issuer", + "kind": "class", + "startLine": 46, + "endLine": 51, + "tokens": 38 + }, + { + "name": "NewIssuer", + "kind": "fn", + "startLine": 53, + "endLine": 63, + "tokens": 88 + }, + { + "name": "acmeUser", + "kind": "class", + "startLine": 66, + "endLine": 70, + "tokens": 33 + }, + { + "name": "GetEmail", + "kind": "method", + "startLine": 72, + "endLine": 72, + "tokens": 23 + }, + { + "name": "GetRegistration", + "kind": "method", + "startLine": 73, + "endLine": 73, + "tokens": 25 + }, + { + "name": "GetPrivateKey", + "kind": "method", + "startLine": 74, + "endLine": 74, + "tokens": 22 + }, + { + "name": "GeneratePrivateKey", + "kind": "fn", + "startLine": 79, + "endLine": 89, + "tokens": 112 + }, + { + "name": "Obtain", + "kind": "method", + "startLine": 96, + "endLine": 137, + "tokens": 306 + }, + { + "name": "client", + "kind": "method", + "startLine": 141, + "endLine": 204, + "tokens": 530 + }, + { + "name": "parsePrivateKey", + "kind": "fn", + "startLine": 206, + "endLine": 223, + "tokens": 148 + }, + { + "name": "certificateExpiry", + "kind": "fn", + "startLine": 226, + "endLine": 236, + "tokens": 106 + } + ], + "symbolSource": "ts", + "skeleton": "type Issuer struct {\nfunc NewIssuer(store port.CertificateStore, email, directoryURL, cloudflareToken string) *Issuer {\ntype acmeUser struct {\nfunc (u *acmeUser) GetEmail() string { return u.email }\nfunc (u *acmeUser) GetRegistration() *registration.Resource { return u.registration }\nfunc (u *acmeUser) GetPrivateKey() crypto.PrivateKey { return u.key }\nfunc GeneratePrivateKey() ([]byte, error) {\nfunc (i *Issuer) Obtain(ctx context.Context, domains []string, privateKeyPEM []byte) (*port.StoredCertificate, error) {\nfunc (i *Issuer) client(ctx context.Context) (*lego.Client, error) {\nfunc parsePrivateKey(pemBytes []byte) (crypto.PrivateKey, error) {\nfunc certificateExpiry(chainPEM []byte) (time.Time, error) {", + "imports": [ + "services/protocols/internal/infrastructure/cloudflare/cloudflare_adapter_test.go", + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0164 + }, + "services/protocols/internal/infrastructure/acme/manager.go": { + "description": "Interface: TlsaPublisher (6 methods)", + "tokens": 2176, + "hash": "f61770b473414e3a", + "size": 7615, + "mtimeMs": 1785635482526.5547, + "updatedAt": "2026-10-06T11:06:20.880Z", + "source": "scan", + "symbols": [ + { + "name": "TlsaPublisher", + "kind": "class", + "startLine": 19, + "endLine": 21, + "tokens": 31 + }, + { + "name": "PendingKeyStore", + "kind": "class", + "startLine": 25, + "endLine": 29, + "tokens": 84 + }, + { + "name": "Manager", + "kind": "class", + "startLine": 34, + "endLine": 46, + "tokens": 76 + }, + { + "name": "NewManager", + "kind": "fn", + "startLine": 48, + "endLine": 67, + "tokens": 130 + }, + { + "name": "GetCertificate", + "kind": "method", + "startLine": 70, + "endLine": 76, + "tokens": 66 + }, + { + "name": "Start", + "kind": "method", + "startLine": 80, + "endLine": 105, + "tokens": 132 + }, + { + "name": "reconcile", + "kind": "method", + "startLine": 110, + "endLine": 132, + "tokens": 168 + }, + { + "name": "reconcileWithDane", + "kind": "method", + "startLine": 134, + "endLine": 226, + "tokens": 868 + }, + { + "name": "issue", + "kind": "method", + "startLine": 229, + "endLine": 244, + "tokens": 160 + }, + { + "name": "serve", + "kind": "method", + "startLine": 248, + "endLine": 255, + "tokens": 79 + } + ], + "symbolSource": "ts", + "skeleton": "type TlsaPublisher interface {\ntype PendingKeyStore interface {\ntype Manager struct {\nfunc NewManager(\nfunc (m *Manager) GetCertificate(*tls.ClientHelloInfo) (*tls.Certificate, error) {\nfunc (m *Manager) Start(ctx context.Context, interval time.Duration) error {\nfunc (m *Manager) reconcile(ctx context.Context) error {\nfunc (m *Manager) reconcileWithDane(ctx context.Context, stored *port.StoredCertificate) error {\nfunc (m *Manager) issue(ctx context.Context, privateKeyPEM []byte, generation int) error {\nfunc (m *Manager) serve(stored *port.StoredCertificate) error {", + "imports": [ + "services/protocols/internal/infrastructure/tls/acme_cert_watcher_test.go", + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/application/usecase/apply_rules_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/arc/canonical.go": { + "description": "implements Authenticated Received Chain (RFC 8617).", + "tokens": 1334, + "hash": "c189910322d4699b", + "size": 4669, + "mtimeMs": 1785635482526.5547, + "updatedAt": "2026-10-06T11:06:20.880Z", + "source": "scan", + "symbols": [ + { + "name": "header", + "kind": "class", + "startLine": 33, + "endLine": 37, + "tokens": 19 + }, + { + "name": "canonicalizeHeaderRelaxed", + "kind": "fn", + "startLine": 42, + "endLine": 51, + "tokens": 99 + }, + { + "name": "canonicalizeBodyRelaxed", + "kind": "fn", + "startLine": 56, + "endLine": 75, + "tokens": 129 + }, + { + "name": "bodyHash", + "kind": "fn", + "startLine": 78, + "endLine": 81, + "tokens": 43 + }, + { + "name": "splitMessage", + "kind": "fn", + "startLine": 85, + "endLine": 119, + "tokens": 242 + }, + { + "name": "sign", + "kind": "fn", + "startLine": 122, + "endLine": 139, + "tokens": 202 + }, + { + "name": "algorithmFor", + "kind": "fn", + "startLine": 142, + "endLine": 147, + "tokens": 49 + } + ], + "symbolSource": "ts", + "importance": 0.015 + }, + "services/protocols/internal/infrastructure/arc/lazy_sealer_test.go": { + "description": "stubKeyRepo (15 fields); methods: FindActiveKeys", + "tokens": 811, + "hash": "7fb2454b4c0a2c2a", + "size": 2837, + "mtimeMs": 1785668486702.2322, + "updatedAt": "2026-10-06T11:06:20.880Z", + "source": "scan", + "symbols": [ + { + "name": "stubKeyRepo", + "kind": "class", + "startLine": 18, + "endLine": 23, + "tokens": 42 + }, + { + "name": "FindActiveKeys", + "kind": "method", + "startLine": 25, + "endLine": 30, + "tokens": 48 + }, + { + "name": "provision", + "kind": "method", + "startLine": 32, + "endLine": 51, + "tokens": 135 + }, + { + "name": "parseTestKey", + "kind": "fn", + "startLine": 53, + "endLine": 56, + "tokens": 41 + }, + { + "name": "TestLazySealer_StartsSealingOnceAKeyIsProvisioned", + "kind": "fn", + "startLine": 66, + "endLine": 90, + "tokens": 241 + }, + { + "name": "TestLazySealer_CachesTheResolvedKey", + "kind": "fn", + "startLine": 93, + "endLine": 108, + "tokens": 128 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/arc/lazy_sealer.go": { + "description": "LazySealer (18 fields); methods: Seal", + "tokens": 682, + "hash": "95b4680d7875a2bd", + "size": 2384, + "mtimeMs": 1785668486702.1443, + "updatedAt": "2026-10-06T11:06:20.880Z", + "source": "scan", + "symbols": [ + { + "name": "KeyParser", + "kind": "class", + "startLine": 13, + "endLine": 13, + "tokens": 19 + }, + { + "name": "LazySealer", + "kind": "class", + "startLine": 22, + "endLine": 30, + "tokens": 48 + }, + { + "name": "NewLazySealer", + "kind": "fn", + "startLine": 34, + "endLine": 36, + "tokens": 56 + }, + { + "name": "Seal", + "kind": "method", + "startLine": 41, + "endLine": 47, + "tokens": 72 + }, + { + "name": "sealer", + "kind": "method", + "startLine": 49, + "endLine": 75, + "tokens": 191 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/arc/sealer_test.go": { + "description": "TestSealer_StartsChainAtInstanceOneWithCvNone, TestSealer_WritesHeadersInPrescribedOrder, TestSealer_ProducesVerifiableChain, TestSealer_ExtendsExi...", + "tokens": 2324, + "hash": "ed4c28593d9abe1c", + "size": 8132, + "mtimeMs": 1785635482526.674, + "updatedAt": "2026-10-06T11:06:20.880Z", + "source": "scan", + "symbols": [ + { + "name": "keyring", + "kind": "class", + "startLine": 24, + "endLine": 24, + "tokens": 12 + }, + { + "name": "lookup", + "kind": "method", + "startLine": 26, + "endLine": 32, + "tokens": 59 + }, + { + "name": "newSealer", + "kind": "fn", + "startLine": 34, + "endLine": 50, + "tokens": 148 + }, + { + "name": "passingResult", + "kind": "fn", + "startLine": 52, + "endLine": 57, + "tokens": 59 + }, + { + "name": "TestSealer_StartsChainAtInstanceOneWithCvNone", + "kind": "fn", + "startLine": 61, + "endLine": 88, + "tokens": 278 + }, + { + "name": "TestSealer_WritesHeadersInPrescribedOrder", + "kind": "fn", + "startLine": 91, + "endLine": 104, + "tokens": 159 + }, + { + "name": "TestSealer_ProducesVerifiableChain", + "kind": "fn", + "startLine": 107, + "endLine": 126, + "tokens": 172 + }, + { + "name": "TestSealer_ExtendsExistingChain", + "kind": "fn", + "startLine": 129, + "endLine": 163, + "tokens": 297 + }, + { + "name": "TestVerifyChain_DetectsBodyTampering", + "kind": "fn", + "startLine": 167, + "endLine": 181, + "tokens": 144 + }, + { + "name": "TestVerifyChain_DetectsHeaderTampering", + "kind": "fn", + "startLine": 184, + "endLine": 194, + "tokens": 139 + }, + { + "name": "TestVerifyChain_NoChainIsNone", + "kind": "fn", + "startLine": 197, + "endLine": 205, + "tokens": 72 + }, + { + "name": "TestSealer_RefusesToExtendFailedChain", + "kind": "fn", + "startLine": 208, + "endLine": 222, + "tokens": 139 + }, + { + "name": "TestBodyHash_IgnoresCosmeticWhitespace", + "kind": "fn", + "startLine": 227, + "endLine": 244, + "tokens": 150 + }, + { + "name": "TestCanonicalizeHeaderRelaxed", + "kind": "fn", + "startLine": 246, + "endLine": 258, + "tokens": 129 + } + ], + "symbolSource": "ts", + "skeleton": "type keyring map[string]crypto.PublicKey\nfunc (k keyring) lookup(domain, selector string) (crypto.PublicKey, error) {\nfunc newSealer(t *testing.T, domain, algorithm string, ring keyring) *Sealer {\nfunc passingResult() port.InboundAuthResult {\nfunc TestSealer_StartsChainAtInstanceOneWithCvNone(t *testing.T) {\nfunc TestSealer_WritesHeadersInPrescribedOrder(t *testing.T) {\nfunc TestSealer_ProducesVerifiableChain(t *testing.T) {\nfunc TestSealer_ExtendsExistingChain(t *testing.T) {\nfunc TestVerifyChain_DetectsBodyTampering(t *testing.T) {\nfunc TestVerifyChain_DetectsHeaderTampering(t *testing.T) {\nfunc TestVerifyChain_NoChainIsNone(t *testing.T) {\nfunc TestSealer_RefusesToExtendFailedChain(t *testing.T) {\nfunc TestBodyHash_IgnoresCosmeticWhitespace(t *testing.T) {\nfunc TestCanonicalizeHeaderRelaxed(t *testing.T) {", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/infrastructure/dkim/keygen.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/arc/sealer.go": { + "description": "Sealer (56 fields); methods: WithClock, Seal", + "tokens": 2407, + "hash": "e098585c2ed25c72", + "size": 8423, + "mtimeMs": 1785635482526.674, + "updatedAt": "2026-10-06T11:06:20.880Z", + "source": "scan", + "symbols": [ + { + "name": "ChainValidation", + "kind": "class", + "startLine": 29, + "endLine": 29, + "tokens": 8 + }, + { + "name": "Sealer", + "kind": "class", + "startLine": 42, + "endLine": 51, + "tokens": 99 + }, + { + "name": "NewSealer", + "kind": "fn", + "startLine": 53, + "endLine": 64, + "tokens": 78 + }, + { + "name": "WithClock", + "kind": "method", + "startLine": 67, + "endLine": 70, + "tokens": 24 + }, + { + "name": "Seal", + "kind": "method", + "startLine": 77, + "endLine": 116, + "tokens": 382 + }, + { + "name": "inspectChain", + "kind": "method", + "startLine": 120, + "endLine": 144, + "tokens": 184 + }, + { + "name": "buildMessageSignature", + "kind": "method", + "startLine": 148, + "endLine": 167, + "tokens": 204 + }, + { + "name": "buildSeal", + "kind": "method", + "startLine": 172, + "endLine": 197, + "tokens": 306 + }, + { + "name": "formatAuthResults", + "kind": "method", + "startLine": 200, + "endLine": 206, + "tokens": 83 + }, + { + "name": "orNone", + "kind": "fn", + "startLine": 208, + "endLine": 213, + "tokens": 25 + }, + { + "name": "signedHeaderList", + "kind": "fn", + "startLine": 217, + "endLine": 228, + "tokens": 78 + }, + { + "name": "canonicalHeaderBlock", + "kind": "fn", + "startLine": 231, + "endLine": 242, + "tokens": 86 + }, + { + "name": "findInstanceHeader", + "kind": "fn", + "startLine": 245, + "endLine": 255, + "tokens": 80 + }, + { + "name": "parseTags", + "kind": "fn", + "startLine": 258, + "endLine": 268, + "tokens": 82 + } + ], + "symbolSource": "ts", + "skeleton": "type ChainValidation string\ntype Sealer struct {\nfunc NewSealer(domain, selector, authServID string, signer crypto.Signer) *Sealer {\nfunc (s *Sealer) WithClock(now func() time.Time) *Sealer {\nfunc (s *Sealer) Seal(_ context.Context, message []byte, authResult port.InboundAuthResult) ([]byte, error) {\nfunc (s *Sealer) inspectChain(headers []header) (instance int, state ChainValidation) {\nfunc (s *Sealer) buildMessageSignature(instance int, timestamp int64, headers []header, body string) (string, error) {\nfunc (s *Sealer) buildSeal(instance int, timestamp int64, state ChainValidation, headers []header, aar, ams string) (string, error) {\nfunc (s *Sealer) formatAuthResults(result port.InboundAuthResult) string {\nfunc orNone(value string) string {\nfunc signedHeaderList(headers []header) []string {\nfunc canonicalHeaderBlock(headers []header, names []string) string {\nfunc findInstanceHeader(headers []header, name string, instance int) (string, bool) {\nfunc parseTags(value string) map[string]string {", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/arc/verifier.go": { + "description": "VerifyChain", + "tokens": 1289, + "hash": "8c7644a1760a7545", + "size": 4510, + "mtimeMs": 1785635482526.674, + "updatedAt": "2026-10-06T11:06:20.880Z", + "source": "scan", + "symbols": [ + { + "name": "VerifyChain", + "kind": "fn", + "startLine": 21, + "endLine": 53, + "tokens": 282 + }, + { + "name": "verifyMessageSignature", + "kind": "fn", + "startLine": 55, + "endLine": 71, + "tokens": 202 + }, + { + "name": "verifySeal", + "kind": "fn", + "startLine": 73, + "endLine": 97, + "tokens": 310 + }, + { + "name": "stripSignature", + "kind": "fn", + "startLine": 101, + "endLine": 107, + "tokens": 41 + }, + { + "name": "verifySignature", + "kind": "fn", + "startLine": 109, + "endLine": 135, + "tokens": 276 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/clamav/clamav_adapter_test.go": { + "description": "TestClamAVAdapter_Scan_CleanAndVirus", + "tokens": 480, + "hash": "2460a8d6d1ddd359", + "size": 1679, + "mtimeMs": 1785635482526.8608, + "updatedAt": "2026-10-06T11:06:20.881Z", + "source": "scan", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go" + ], + "importance": 0.0181 + }, + "services/protocols/internal/infrastructure/clamav/clamav_adapter.go": { + "description": "ClamAVAdapter (28 fields); methods: Scan", + "tokens": 745, + "hash": "c65d853e92e724de", + "size": 2607, + "mtimeMs": 1785635482526.674, + "updatedAt": "2026-10-06T11:06:20.881Z", + "source": "scan", + "symbols": [ + { + "name": "ClamAVAdapter", + "kind": "class", + "startLine": 17, + "endLine": 20, + "tokens": 20 + }, + { + "name": "NewClamAVAdapter", + "kind": "fn", + "startLine": 22, + "endLine": 30, + "tokens": 53 + }, + { + "name": "Scan", + "kind": "method", + "startLine": 32, + "endLine": 106, + "tokens": 612 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/cloudflare/cloudflare_adapter_test.go": { + "description": "TestCloudflareAdapter_GetZoneID_List_Create_Update_Delete", + "tokens": 835, + "hash": "f1f1fbbcce0e152d", + "size": 2920, + "mtimeMs": 1785635482526.9717, + "updatedAt": "2026-10-06T11:06:20.881Z", + "source": "scan", + "symbols": [ + { + "name": "TestCloudflareAdapter_GetZoneID_List_Create_Update_Delete", + "kind": "fn", + "startLine": 13, + "endLine": 115, + "tokens": 791 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go", + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0261 + }, + "services/protocols/internal/infrastructure/cloudflare/cloudflare_adapter.go": { + "description": "CloudflareAdapter (115 fields); methods: GetZoneID, ListRecords, CreateRecord, UpdateRecord", + "tokens": 3382, + "hash": "ae303cd61b7c5294", + "size": 11835, + "mtimeMs": 1787571041892.1978, + "updatedAt": "2026-10-06T11:06:20.881Z", + "source": "scan", + "symbols": [ + { + "name": "CloudflareAdapter", + "kind": "class", + "startLine": 27, + "endLine": 31, + "tokens": 28 + }, + { + "name": "NewCloudflareAdapter", + "kind": "fn", + "startLine": 33, + "endLine": 41, + "tokens": 105 + }, + { + "name": "cfError", + "kind": "class", + "startLine": 43, + "endLine": 45, + "tokens": 16 + }, + { + "name": "cfResultInfo", + "kind": "class", + "startLine": 47, + "endLine": 50, + "tokens": 28 + }, + { + "name": "cfZoneResponse", + "kind": "class", + "startLine": 52, + "endLine": 59, + "tokens": 54 + }, + { + "name": "cfRecord", + "kind": "class", + "startLine": 61, + "endLine": 76, + "tokens": 205 + }, + { + "name": "cfRecordResponse", + "kind": "class", + "startLine": 78, + "endLine": 83, + "tokens": 58 + }, + { + "name": "cfSingleRecordResponse", + "kind": "class", + "startLine": 85, + "endLine": 89, + "tokens": 42 + }, + { + "name": "GetZoneID", + "kind": "method", + "startLine": 91, + "endLine": 104, + "tokens": 150 + }, + { + "name": "ListRecords", + "kind": "method", + "startLine": 106, + "endLine": 154, + "tokens": 450 + }, + { + "name": "CreateRecord", + "kind": "method", + "startLine": 156, + "endLine": 167, + "tokens": 154 + }, + { + "name": "UpdateRecord", + "kind": "method", + "startLine": 169, + "endLine": 180, + "tokens": 157 + }, + { + "name": "DeleteRecord", + "kind": "method", + "startLine": 182, + "endLine": 193, + "tokens": 144 + }, + { + "name": "toCfRecord", + "kind": "fn", + "startLine": 198, + "endLine": 225, + "tokens": 183 + }, + { + "name": "structuredData", + "kind": "fn", + "startLine": 230, + "endLine": 272, + "tokens": 314 + }, + { + "name": "renderStructuredValue", + "kind": "fn", + "startLine": 277, + "endLine": 303, + "tokens": 210 + }, + { + "name": "doJSON", + "kind": "method", + "startLine": 308, + "endLine": 368, + "tokens": 477 + }, + { + "name": "describeErrors", + "kind": "fn", + "startLine": 370, + "endLine": 383, + "tokens": 98 + }, + { + "name": "retryAfter", + "kind": "fn", + "startLine": 388, + "endLine": 397, + "tokens": 77 + } + ], + "symbolSource": "ts", + "skeleton": "type CloudflareAdapter struct {\nfunc NewCloudflareAdapter(apiToken string) *CloudflareAdapter {\ntype cfError struct {\ntype cfResultInfo struct {\ntype cfZoneResponse struct {\ntype cfRecord struct {\ntype cfRecordResponse struct {\ntype cfSingleRecordResponse struct {\nfunc (a *CloudflareAdapter) GetZoneID(ctx context.Context, domainName string) (string, error) {\nfunc (a *CloudflareAdapter) ListRecords(ctx context.Context, zoneID string) ([]entity.DnsRecord, error) {\nfunc (a *CloudflareAdapter) CreateRecord(ctx context.Context, zoneID string, record entity.DnsRecord) error {\nfunc (a *CloudflareAdapter) UpdateRecord(ctx context.Context, zoneID string, record entity.DnsRecord) error {\nfunc (a *CloudflareAdapter) DeleteRecord(ctx context.Context, zoneID string, recordID string) error {\nfunc toCfRecord(record entity.DnsRecord) cfRecord {\nfunc structuredData(record entity.DnsRecord) map[string]any {\nfunc renderStructuredValue(recordType string, data map[string]any) string {\nfunc (a *CloudflareAdapter) doJSON(ctx context.Context, method, reqURL string, body any, out any, errsOf func() []cfError) error {\nfunc describeErrors(errs []cfError, raw []byte) string {\nfunc retryAfter(header string, fallback time.Duration) time.Duration {", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go", + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/cloudflare/cloudflare_hardening_test.go": { + "description": "TestCloudflareAdapter_CreateRecord_SendsProxiedFalseExplicitly, TestCloudflareAdapter_ReportsHttpErrorStatus, TestCloudflareAdapter_ListRecords_Fol...", + "tokens": 2754, + "hash": "6681fc17403131e5", + "size": 9639, + "mtimeMs": 1787571024467.825, + "updatedAt": "2026-10-06T11:06:20.881Z", + "source": "scan", + "symbols": [ + { + "name": "TestCloudflareAdapter_CreateRecord_SendsProxiedFalseExplicitly", + "kind": "fn", + "startLine": 18, + "endLine": 44, + "tokens": 262 + }, + { + "name": "TestCloudflareAdapter_ReportsHttpErrorStatus", + "kind": "fn", + "startLine": 48, + "endLine": 65, + "tokens": 184 + }, + { + "name": "TestCloudflareAdapter_ListRecords_FollowsPagination", + "kind": "fn", + "startLine": 70, + "endLine": 103, + "tokens": 320 + }, + { + "name": "TestCloudflareAdapter_RetriesOnRateLimit", + "kind": "fn", + "startLine": 106, + "endLine": 136, + "tokens": 241 + }, + { + "name": "TestCloudflareAdapter_DeleteRecord_ReportsFailure", + "kind": "fn", + "startLine": 140, + "endLine": 153, + "tokens": 149 + }, + { + "name": "TestCloudflareAdapter_SendsStructuredDataForSrvAndTlsa", + "kind": "fn", + "startLine": 157, + "endLine": 209, + "tokens": 517 + }, + { + "name": "TestCloudflareAdapter_ListRecords_RendersStructuredValues", + "kind": "fn", + "startLine": 214, + "endLine": 237, + "tokens": 262 + }, + { + "name": "TestCloudflareAdapter_ListRecords_KeepsSrvPriority", + "kind": "fn", + "startLine": 244, + "endLine": 280, + "tokens": 350 + } + ], + "symbolSource": "ts", + "skeleton": "func TestCloudflareAdapter_CreateRecord_SendsProxiedFalseExplicitly(t *testing.T) {\nfunc TestCloudflareAdapter_ReportsHttpErrorStatus(t *testing.T) {\nfunc TestCloudflareAdapter_ListRecords_FollowsPagination(t *testing.T) {\nfunc TestCloudflareAdapter_RetriesOnRateLimit(t *testing.T) {\nfunc TestCloudflareAdapter_DeleteRecord_ReportsFailure(t *testing.T) {\nfunc TestCloudflareAdapter_SendsStructuredDataForSrvAndTlsa(t *testing.T) {\nfunc TestCloudflareAdapter_ListRecords_RendersStructuredValues(t *testing.T) {\nfunc TestCloudflareAdapter_ListRecords_KeepsSrvPriority(t *testing.T) {", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go", + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/diskstorage/local_disk_blob_reader_test.go": { + "description": "TestLocalDiskBlobReader_ReadByID_ReturnsStoredContent, TestLocalDiskBlobReader_ReadByID_ReturnsErrorForUnknownID", + "tokens": 327, + "hash": "eb891a6a08f1d186", + "size": 1144, + "mtimeMs": 1785635482527.1055, + "updatedAt": "2026-10-06T11:06:20.881Z", + "source": "scan", + "importance": 0.0524 + }, + "services/protocols/internal/infrastructure/diskstorage/local_disk_blob_reader.go": { + "description": "LocalDiskBlobReader (7 fields); methods: ReadByID", + "tokens": 248, + "hash": "4beb3cd11c6ca205", + "size": 868, + "mtimeMs": 1785635482526.9717, + "updatedAt": "2026-10-06T11:06:20.881Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/diskstorage/local_disk_blob_storage_test.go": { + "description": "TestLocalDiskBlobStorage_Store_WritesFileAndInsertsBlobRow, TestLocalDiskBlobStorage_Store_DedupsIdenticalContent", + "tokens": 676, + "hash": "f9d904580d2458f7", + "size": 2366, + "mtimeMs": 1785635482527.1055, + "updatedAt": "2026-10-06T11:06:20.881Z", + "source": "scan", + "symbols": [ + { + "name": "testPoolForBlobStorage", + "kind": "fn", + "startLine": 13, + "endLine": 27, + "tokens": 135 + }, + { + "name": "TestLocalDiskBlobStorage_Store_WritesFileAndInsertsBlobRow", + "kind": "fn", + "startLine": 29, + "endLine": 60, + "tokens": 285 + }, + { + "name": "TestLocalDiskBlobStorage_Store_DedupsIdenticalContent", + "kind": "fn", + "startLine": 62, + "endLine": 82, + "tokens": 221 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/diskstorage/local_disk_blob_storage.go": { + "description": "implements port.BlobStorage on the local filesystem.", + "tokens": 992, + "hash": "89f1d26b2fc0ba8d", + "size": 3469, + "mtimeMs": 1785635482527.1055, + "updatedAt": "2026-10-06T11:06:20.882Z", + "source": "scan", + "symbols": [ + { + "name": "LocalDiskBlobStorage", + "kind": "class", + "startLine": 23, + "endLine": 26, + "tokens": 22 + }, + { + "name": "NewLocalDiskBlobStorage", + "kind": "fn", + "startLine": 28, + "endLine": 30, + "tokens": 43 + }, + { + "name": "Store", + "kind": "method", + "startLine": 32, + "endLine": 101, + "tokens": 779 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/dkim/keygen.go": { + "description": "generates, stores and applies the DKIM signing keys described", + "tokens": 981, + "hash": "e9ee92ef901cf55b", + "size": 3433, + "mtimeMs": 1785635482527.1055, + "updatedAt": "2026-10-06T11:06:20.882Z", + "source": "scan", + "symbols": [ + { + "name": "GeneratedKey", + "kind": "class", + "startLine": 27, + "endLine": 36, + "tokens": 126 + }, + { + "name": "Generate", + "kind": "fn", + "startLine": 39, + "endLine": 48, + "tokens": 76 + }, + { + "name": "generateRSA", + "kind": "fn", + "startLine": 50, + "endLine": 71, + "tokens": 158 + }, + { + "name": "generateEd25519", + "kind": "fn", + "startLine": 73, + "endLine": 90, + "tokens": 139 + }, + { + "name": "marshalPrivatePEM", + "kind": "fn", + "startLine": 92, + "endLine": 98, + "tokens": 76 + }, + { + "name": "ParsePrivateKey", + "kind": "fn", + "startLine": 103, + "endLine": 119, + "tokens": 134 + } + ], + "symbolSource": "ts", + "importance": 0.0409 + }, + "services/protocols/internal/infrastructure/dkim/signer_test.go": { + "description": "fakeKeyRepo (20 fields); methods: FindActiveKeys", + "tokens": 1127, + "hash": "ef450586499fecea", + "size": 3942, + "mtimeMs": 1785635482527.2583, + "updatedAt": "2026-10-06T11:06:20.882Z", + "source": "scan", + "symbols": [ + { + "name": "fakeKeyRepo", + "kind": "class", + "startLine": 11, + "endLine": 14, + "tokens": 20 + }, + { + "name": "FindActiveKeys", + "kind": "method", + "startLine": 16, + "endLine": 18, + "tokens": 35 + }, + { + "name": "keyFor", + "kind": "fn", + "startLine": 28, + "endLine": 40, + "tokens": 102 + }, + { + "name": "TestSigner_AppliesBothAlgorithms", + "kind": "fn", + "startLine": 44, + "endLine": 71, + "tokens": 262 + }, + { + "name": "TestSigner_ProducesVerifiableSignature", + "kind": "fn", + "startLine": 74, + "endLine": 100, + "tokens": 247 + }, + { + "name": "TestSigner_NoKeysLeavesMessageUnchanged", + "kind": "fn", + "startLine": 103, + "endLine": 111, + "tokens": 94 + }, + { + "name": "TestGenerate_RejectsUnknownAlgorithm", + "kind": "fn", + "startLine": 113, + "endLine": 117, + "tokens": 49 + }, + { + "name": "TestParsePrivateKey_RoundTripsBothAlgorithms", + "kind": "fn", + "startLine": 119, + "endLine": 132, + "tokens": 143 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/dkim/signer.go": { + "description": "Signer (16 fields); methods: Sign", + "tokens": 732, + "hash": "88db97c75ea6436b", + "size": 2561, + "mtimeMs": 1785635482527.2583, + "updatedAt": "2026-10-06T11:06:20.882Z", + "source": "scan", + "symbols": [ + { + "name": "Signer", + "kind": "class", + "startLine": 24, + "endLine": 26, + "tokens": 15 + }, + { + "name": "NewSigner", + "kind": "fn", + "startLine": 28, + "endLine": 30, + "tokens": 24 + }, + { + "name": "Sign", + "kind": "method", + "startLine": 38, + "endLine": 59, + "tokens": 153 + }, + { + "name": "signOnce", + "kind": "fn", + "startLine": 61, + "endLine": 84, + "tokens": 232 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/infrastructure/dkim/keygen.go", + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/mailauth/authenticator_test.go": { + "description": "fakeZone (34 fields); methods: LookupTXT, LookupIPAddr, LookupMX, LookupAddr", + "tokens": 2301, + "hash": "68f9dd57afd37705", + "size": 8051, + "mtimeMs": 1785635482527.3936, + "updatedAt": "2026-10-06T11:06:20.882Z", + "source": "scan", + "symbols": [ + { + "name": "fakeZone", + "kind": "class", + "startLine": 18, + "endLine": 21, + "tokens": 22 + }, + { + "name": "LookupTXT", + "kind": "method", + "startLine": 23, + "endLine": 25, + "tokens": 31 + }, + { + "name": "lookupTXT", + "kind": "method", + "startLine": 27, + "endLine": 33, + "tokens": 69 + }, + { + "name": "LookupIPAddr", + "kind": "method", + "startLine": 35, + "endLine": 45, + "tokens": 102 + }, + { + "name": "LookupMX", + "kind": "method", + "startLine": 47, + "endLine": 49, + "tokens": 46 + }, + { + "name": "LookupAddr", + "kind": "method", + "startLine": 51, + "endLine": 53, + "tokens": 46 + }, + { + "name": "newTestAuthenticator", + "kind": "fn", + "startLine": 55, + "endLine": 57, + "tokens": 42 + }, + { + "name": "TestAuthenticate_SpfPassAlignsDmarc", + "kind": "fn", + "startLine": 69, + "endLine": 100, + "tokens": 338 + }, + { + "name": "TestAuthenticate_SpfFailWithoutDkimFailsDmarc", + "kind": "fn", + "startLine": 104, + "endLine": 128, + "tokens": 218 + }, + { + "name": "TestAuthenticate_DkimPassAlignsDmarcDespiteSpfFail", + "kind": "fn", + "startLine": 132, + "endLine": 178, + "tokens": 449 + }, + { + "name": "TestAuthenticate_TamperedBodyFailsDkim", + "kind": "fn", + "startLine": 181, + "endLine": 206, + "tokens": 271 + }, + { + "name": "TestAuthenticate_NoDmarcRecordYieldsNone", + "kind": "fn", + "startLine": 209, + "endLine": 224, + "tokens": 155 + }, + { + "name": "TestAuthenticate_EmptyEnvelopeFromChecksHelo", + "kind": "fn", + "startLine": 228, + "endLine": 243, + "tokens": 146 + } + ], + "symbolSource": "ts", + "skeleton": "type fakeZone struct {\nfunc (z *fakeZone) LookupTXT(_ context.Context, name string) ([]string, error) {\nfunc (z *fakeZone) lookupTXT(name string) ([]string, error) {\nfunc (z *fakeZone) LookupIPAddr(_ context.Context, name string) ([]net.IPAddr, error) {\nfunc (z *fakeZone) LookupMX(_ context.Context, name string) ([]*net.MX, error) {\nfunc (z *fakeZone) LookupAddr(_ context.Context, addr string) ([]string, error) {\nfunc newTestAuthenticator(zone *fakeZone) *Authenticator {\nfunc TestAuthenticate_SpfPassAlignsDmarc(t *testing.T) {\nfunc TestAuthenticate_SpfFailWithoutDkimFailsDmarc(t *testing.T) {\nfunc TestAuthenticate_DkimPassAlignsDmarcDespiteSpfFail(t *testing.T) {\nfunc TestAuthenticate_TamperedBodyFailsDkim(t *testing.T) {\nfunc TestAuthenticate_NoDmarcRecordYieldsNone(t *testing.T) {\nfunc TestAuthenticate_EmptyEnvelopeFromChecksHelo(t *testing.T) {", + "imports": [ + "services/protocols/internal/infrastructure/dkim/keygen.go", + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.015 + }, + "services/protocols/internal/infrastructure/mailauth/authenticator.go": { + "description": "runs the inbound authentication chain of PLAN.md section", + "tokens": 1950, + "hash": "b998c868bcb007b3", + "size": 6825, + "mtimeMs": 1785635482527.2583, + "updatedAt": "2026-10-06T11:06:20.882Z", + "source": "scan", + "symbols": [ + { + "name": "LookupTXTFunc", + "kind": "class", + "startLine": 23, + "endLine": 23, + "tokens": 16 + }, + { + "name": "Authenticator", + "kind": "class", + "startLine": 26, + "endLine": 33, + "tokens": 83 + }, + { + "name": "NewAuthenticator", + "kind": "fn", + "startLine": 35, + "endLine": 40, + "tokens": 62 + }, + { + "name": "WithLookupTXT", + "kind": "method", + "startLine": 44, + "endLine": 48, + "tokens": 47 + }, + { + "name": "Authenticate", + "kind": "method", + "startLine": 50, + "endLine": 68, + "tokens": 190 + }, + { + "name": "checkSPF", + "kind": "method", + "startLine": 70, + "endLine": 93, + "tokens": 210 + }, + { + "name": "checkDKIM", + "kind": "method", + "startLine": 95, + "endLine": 122, + "tokens": 261 + }, + { + "name": "checkDMARC", + "kind": "method", + "startLine": 124, + "endLine": 157, + "tokens": 306 + }, + { + "name": "aligned", + "kind": "fn", + "startLine": 161, + "endLine": 173, + "tokens": 106 + }, + { + "name": "formatAuthResults", + "kind": "method", + "startLine": 177, + "endLine": 197, + "tokens": 172 + }, + { + "name": "headerFromDomain", + "kind": "fn", + "startLine": 201, + "endLine": 211, + "tokens": 85 + }, + { + "name": "domainOf", + "kind": "fn", + "startLine": 213, + "endLine": 218, + "tokens": 50 + } + ], + "symbolSource": "ts", + "imports": [ + "services/webmail/src/app/(user)/user/mail/[folder]/page.tsx", + "services/protocols/internal/infrastructure/dkim/keygen.go", + "services/webmail/src/app/(admin)/admin/dmarc/page.tsx", + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/netdns/mx_resolver.go": { + "description": "NetMXResolver (10 fields); methods: LookupMX", + "tokens": 197, + "hash": "af6e3fef1534b466", + "size": 687, + "mtimeMs": 1785635482527.3936, + "updatedAt": "2026-10-06T11:06:20.882Z", + "source": "scan", + "importance": 0.017 + }, + "services/protocols/internal/infrastructure/netdns/public_verifier_match_test.go": { + "description": "Comparing a desired record against what a resolver actually answers.", + "tokens": 1121, + "hash": "43c0ae5587defec8", + "size": 3923, + "mtimeMs": 1787496012191.9895, + "updatedAt": "2026-10-06T11:06:20.882Z", + "source": "scan", + "symbols": [ + { + "name": "TestMxIgnoresThePriorityAndTrailingDot", + "kind": "fn", + "startLine": 12, + "endLine": 27, + "tokens": 233 + }, + { + "name": "TestLongTxtIsJoinedWithoutASeparator", + "kind": "fn", + "startLine": 34, + "endLine": 39, + "tokens": 69 + }, + { + "name": "TestShortTxtStillMatches", + "kind": "fn", + "startLine": 41, + "endLine": 45, + "tokens": 51 + }, + { + "name": "TestTxtKeepsItsOwnSpaces", + "kind": "fn", + "startLine": 48, + "endLine": 56, + "tokens": 100 + }, + { + "name": "TestCnameIgnoresTheTrailingDot", + "kind": "fn", + "startLine": 58, + "endLine": 62, + "tokens": 58 + }, + { + "name": "TestCaseDoesNotDecide", + "kind": "fn", + "startLine": 64, + "endLine": 68, + "tokens": 57 + }, + { + "name": "TestAValueIsCompared", + "kind": "fn", + "startLine": 70, + "endLine": 77, + "tokens": 80 + }, + { + "name": "TestProxiedCnameIsQueriedAsTheTypeTheProxyAnswers", + "kind": "fn", + "startLine": 83, + "endLine": 96, + "tokens": 154 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/netdns/public_verifier.go": { + "description": "PublicVerifier (35 fields); methods: VerifyRecord", + "tokens": 1718, + "hash": "77518df407ccb0e7", + "size": 6012, + "mtimeMs": 1787496012216.8523, + "updatedAt": "2026-10-06T11:06:20.883Z", + "source": "scan", + "symbols": [ + { + "name": "PublicVerifier", + "kind": "class", + "startLine": 20, + "endLine": 23, + "tokens": 21 + }, + { + "name": "NewPublicVerifier", + "kind": "fn", + "startLine": 25, + "endLine": 40, + "tokens": 116 + }, + { + "name": "VerifyRecord", + "kind": "method", + "startLine": 42, + "endLine": 83, + "tokens": 440 + }, + { + "name": "answerMatches", + "kind": "fn", + "startLine": 86, + "endLine": 99, + "tokens": 112 + }, + { + "name": "answerMatchesValue", + "kind": "fn", + "startLine": 109, + "endLine": 123, + "tokens": 178 + }, + { + "name": "mxHost", + "kind": "fn", + "startLine": 126, + "endLine": 132, + "tokens": 51 + }, + { + "name": "normalizeName", + "kind": "fn", + "startLine": 135, + "endLine": 140, + "tokens": 62 + }, + { + "name": "joinTxtStrings", + "kind": "fn", + "startLine": 149, + "endLine": 175, + "tokens": 160 + }, + { + "name": "queryTypeFor", + "kind": "fn", + "startLine": 182, + "endLine": 188, + "tokens": 47 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/netdns/tlsa_resolver.go": { + "description": "TlsaResolver (14 fields); methods: LookupTLSA", + "tokens": 648, + "hash": "4e597220dc37c687", + "size": 2266, + "mtimeMs": 1785635482527.4885, + "updatedAt": "2026-10-06T11:06:20.883Z", + "source": "scan", + "symbols": [ + { + "name": "TlsaResolver", + "kind": "class", + "startLine": 18, + "endLine": 22, + "tokens": 39 + }, + { + "name": "NewTlsaResolver", + "kind": "fn", + "startLine": 24, + "endLine": 35, + "tokens": 81 + }, + { + "name": "LookupTLSA", + "kind": "method", + "startLine": 37, + "endLine": 77, + "tokens": 402 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/acme_repository.go": { + "description": "AcmeRepository (64 fields); methods: LoadCertificate, SaveCertificate, LoadAccountKey, SaveAccountKey", + "tokens": 1995, + "hash": "a01a2314ca7df385", + "size": 6980, + "mtimeMs": 1785635482527.4885, + "updatedAt": "2026-10-06T11:06:20.883Z", + "source": "scan", + "symbols": [ + { + "name": "AcmeRepository", + "kind": "class", + "startLine": 18, + "endLine": 22, + "tokens": 32 + }, + { + "name": "NewAcmeRepository", + "kind": "fn", + "startLine": 24, + "endLine": 26, + "tokens": 52 + }, + { + "name": "LoadCertificate", + "kind": "method", + "startLine": 28, + "endLine": 61, + "tokens": 288 + }, + { + "name": "SaveCertificate", + "kind": "method", + "startLine": 63, + "endLine": 88, + "tokens": 288 + }, + { + "name": "LoadAccountKey", + "kind": "method", + "startLine": 90, + "endLine": 109, + "tokens": 142 + }, + { + "name": "SaveAccountKey", + "kind": "method", + "startLine": 111, + "endLine": 130, + "tokens": 199 + }, + { + "name": "LoadPendingKey", + "kind": "method", + "startLine": 132, + "endLine": 151, + "tokens": 146 + }, + { + "name": "SavePendingKey", + "kind": "method", + "startLine": 156, + "endLine": 158, + "tokens": 50 + }, + { + "name": "SavePendingKeyWithDigest", + "kind": "method", + "startLine": 160, + "endLine": 181, + "tokens": 237 + }, + { + "name": "PendingKeyDigest", + "kind": "method", + "startLine": 185, + "endLine": 197, + "tokens": 136 + }, + { + "name": "MarkPendingKeyPublished", + "kind": "method", + "startLine": 201, + "endLine": 209, + "tokens": 97 + }, + { + "name": "ClearPendingKey", + "kind": "method", + "startLine": 211, + "endLine": 217, + "tokens": 78 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/infrastructure/vault/secret_vault_test.go" + ], + "importance": 0.241 + }, + "services/protocols/internal/infrastructure/postgres/alias_repository_test.go": { + "description": "TestAliasRepository_EnsureSystemAliases", + "tokens": 342, + "hash": "792cbc44106fc0a5", + "size": 1194, + "mtimeMs": 1785635482527.6165, + "updatedAt": "2026-10-06T11:06:20.883Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/alias_repository.go": { + "description": "AliasRepository (15 fields); methods: EnsureSystemAliases", + "tokens": 388, + "hash": "e20cf172b0c85566", + "size": 1355, + "mtimeMs": 1785635482527.6165, + "updatedAt": "2026-10-06T11:06:20.883Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/attachment_backfill.go": { + "description": "AttachmentBackfill (24 fields); methods: Done, MarkDone, Candidates, SetHasAttachments", + "tokens": 749, + "hash": "092f8a813edc1aff", + "size": 2620, + "mtimeMs": 1787585031466.8464, + "updatedAt": "2026-10-06T11:06:20.883Z", + "source": "scan", + "symbols": [ + { + "name": "AttachmentBackfill", + "kind": "class", + "startLine": 17, + "endLine": 19, + "tokens": 16 + }, + { + "name": "NewAttachmentBackfill", + "kind": "fn", + "startLine": 21, + "endLine": 23, + "tokens": 32 + }, + { + "name": "Done", + "kind": "method", + "startLine": 26, + "endLine": 34, + "tokens": 92 + }, + { + "name": "MarkDone", + "kind": "method", + "startLine": 36, + "endLine": 44, + "tokens": 91 + }, + { + "name": "Candidate", + "kind": "class", + "startLine": 47, + "endLine": 50, + "tokens": 18 + }, + { + "name": "Candidates", + "kind": "method", + "startLine": 56, + "endLine": 77, + "tokens": 166 + }, + { + "name": "SetHasAttachments", + "kind": "method", + "startLine": 80, + "endLine": 87, + "tokens": 88 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/auth_repository_test.go": { + "description": "TestAuthRepository_FindByAddress_ReturnsHashForActiveMailbox, TestAuthRepository_FindByAddress_ReturnsNilForUnknownAddress", + "tokens": 475, + "hash": "210c9525a62663e6", + "size": 1661, + "mtimeMs": 1785635482527.6165, + "updatedAt": "2026-10-06T11:06:20.883Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/auth_repository.go": { + "description": "AuthRepository (13 fields); methods: FindByAddress", + "tokens": 299, + "hash": "f90e0d7457b04a88", + "size": 1044, + "mtimeMs": 1785635482527.6165, + "updatedAt": "2026-10-06T11:06:20.883Z", + "source": "scan", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/bimi_repository.go": { + "description": "BimiRepository (24 fields); methods: Save, Load, Delete", + "tokens": 613, + "hash": "d9090a76482b617f", + "size": 2144, + "mtimeMs": 1788830725028.156, + "updatedAt": "2026-10-06T11:06:20.883Z", + "source": "scan", + "symbols": [ + { + "name": "BimiRepository", + "kind": "class", + "startLine": 15, + "endLine": 17, + "tokens": 15 + }, + { + "name": "NewBimiRepository", + "kind": "fn", + "startLine": 19, + "endLine": 21, + "tokens": 28 + }, + { + "name": "BimiLogo", + "kind": "class", + "startLine": 24, + "endLine": 28, + "tokens": 20 + }, + { + "name": "Save", + "kind": "method", + "startLine": 31, + "endLine": 46, + "tokens": 185 + }, + { + "name": "Load", + "kind": "method", + "startLine": 49, + "endLine": 67, + "tokens": 153 + }, + { + "name": "Delete", + "kind": "method", + "startLine": 70, + "endLine": 79, + "tokens": 91 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/copy_repository_test.go": { + "description": "TestCopyRepository_CopyMessages_AllocatesNewUIDsAndPreservesFlags", + "tokens": 1214, + "hash": "143cd9a29cae2cb1", + "size": 4246, + "mtimeMs": 1785668486702.3618, + "updatedAt": "2026-10-06T11:06:20.883Z", + "source": "scan", + "symbols": [ + { + "name": "TestCopyRepository_CopyMessages_AllocatesNewUIDsAndPreservesFlags", + "kind": "fn", + "startLine": 11, + "endLine": 110, + "tokens": 1176 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/copy_repository.go": { + "description": "CopyRepository (54 fields); methods: CopyMessages", + "tokens": 1610, + "hash": "415dec20ce8a1714", + "size": 5634, + "mtimeMs": 1785635482527.6165, + "updatedAt": "2026-10-06T11:06:20.883Z", + "source": "scan", + "symbols": [ + { + "name": "CopyRepository", + "kind": "class", + "startLine": 16, + "endLine": 18, + "tokens": 15 + }, + { + "name": "NewCopyRepository", + "kind": "fn", + "startLine": 20, + "endLine": 22, + "tokens": 28 + }, + { + "name": "CopyMessages", + "kind": "method", + "startLine": 24, + "endLine": 34, + "tokens": 138 + }, + { + "name": "copyOne", + "kind": "method", + "startLine": 36, + "endLine": 134, + "tokens": 1320 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/delivered_report_store.go": { + "description": "DeliveredReportStore (65 fields); methods: ListUningestedReports, MoveToReportsFolder", + "tokens": 1366, + "hash": "ace73d1ae4d6d1d8", + "size": 4779, + "mtimeMs": 1787316155298.548, + "updatedAt": "2026-10-06T11:06:20.884Z", + "source": "scan", + "symbols": [ + { + "name": "DeliveredReportStore", + "kind": "class", + "startLine": 15, + "endLine": 17, + "tokens": 16 + }, + { + "name": "NewDeliveredReportStore", + "kind": "fn", + "startLine": 19, + "endLine": 21, + "tokens": 34 + }, + { + "name": "ListUningestedReports", + "kind": "method", + "startLine": 34, + "endLine": 66, + "tokens": 264 + }, + { + "name": "MoveToReportsFolder", + "kind": "method", + "startLine": 77, + "endLine": 156, + "tokens": 678 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/dkim_repository.go": { + "description": "DkimRepository (73 fields); methods: FindActiveKeys, FindPublicKey, Insert, ListKeys", + "tokens": 2032, + "hash": "951ca04edbe7f042", + "size": 7110, + "mtimeMs": 1785689161518.8433, + "updatedAt": "2026-10-06T11:06:20.884Z", + "source": "scan", + "symbols": [ + { + "name": "DkimRepository", + "kind": "class", + "startLine": 19, + "endLine": 22, + "tokens": 22 + }, + { + "name": "NewDkimRepository", + "kind": "fn", + "startLine": 24, + "endLine": 26, + "tokens": 38 + }, + { + "name": "FindActiveKeys", + "kind": "method", + "startLine": 28, + "endLine": 66, + "tokens": 336 + }, + { + "name": "FindPublicKey", + "kind": "method", + "startLine": 71, + "endLine": 87, + "tokens": 151 + }, + { + "name": "Insert", + "kind": "method", + "startLine": 93, + "endLine": 122, + "tokens": 318 + }, + { + "name": "DkimKeyInfo", + "kind": "class", + "startLine": 129, + "endLine": 138, + "tokens": 104 + }, + { + "name": "ListKeys", + "kind": "method", + "startLine": 141, + "endLine": 164, + "tokens": 222 + }, + { + "name": "Rotate", + "kind": "method", + "startLine": 173, + "endLine": 210, + "tokens": 377 + } + ], + "symbolSource": "ts", + "skeleton": "type DkimRepository struct {\nfunc NewDkimRepository(pool *pgxpool.Pool, v *vault.SecretVault) *DkimRepository {\nfunc (r *DkimRepository) FindActiveKeys(ctx context.Context, domainName string) ([]port.DkimSigningKey, error) {\nfunc (r *DkimRepository) FindPublicKey(ctx context.Context, domainName, algorithm string) (string, error) {\nfunc (r *DkimRepository) Insert(ctx context.Context, domainName, selector, algorithm string, privateKeyPEM []byte, publicKey string) error {\ntype DkimKeyInfo struct {\nfunc (r *DkimRepository) ListKeys(ctx context.Context, domainName string) ([]DkimKeyInfo, error) {\nfunc (r *DkimRepository) Rotate(", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/infrastructure/vault/secret_vault_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/domain_repository.go": { + "description": "DomainRepository (18 fields); methods: ActiveDomainNames, SaveDnsStatus", + "tokens": 711, + "hash": "a889ca053bfe4d30", + "size": 2488, + "mtimeMs": 1787571965123.277, + "updatedAt": "2026-10-06T11:06:20.884Z", + "source": "scan", + "symbols": [ + { + "name": "DomainRepository", + "kind": "class", + "startLine": 12, + "endLine": 14, + "tokens": 15 + }, + { + "name": "NewDomainRepository", + "kind": "fn", + "startLine": 16, + "endLine": 18, + "tokens": 30 + }, + { + "name": "ActiveDomainNames", + "kind": "method", + "startLine": 25, + "endLine": 43, + "tokens": 130 + }, + { + "name": "SaveDnsStatus", + "kind": "method", + "startLine": 50, + "endLine": 60, + "tokens": 102 + }, + { + "name": "columnStatus", + "kind": "fn", + "startLine": 68, + "endLine": 79, + "tokens": 113 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/domain_status_test.go": { + "description": "dns_status is constrained by the schema to a fixed set. The verifier answers", + "tokens": 281, + "hash": "4b3f1a7c4b2c84ba", + "size": 981, + "mtimeMs": 1787571965128.0374, + "updatedAt": "2026-10-06T11:06:20.884Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/expunge_repository_test.go": { + "description": "TestExpungeRepository_Expunge_SetsExpungedAtAndDecrementsCounters", + "tokens": 1063, + "hash": "1fa79b23ad86c2d1", + "size": 3718, + "mtimeMs": 1785668486702.4778, + "updatedAt": "2026-10-06T11:06:20.884Z", + "source": "scan", + "symbols": [ + { + "name": "TestExpungeRepository_Expunge_SetsExpungedAtAndDecrementsCounters", + "kind": "fn", + "startLine": 10, + "endLine": 96, + "tokens": 1040 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/expunge_repository.go": { + "description": "ExpungeRepository (34 fields); methods: Expunge", + "tokens": 828, + "hash": "c4907970f28c56ab", + "size": 2896, + "mtimeMs": 1785635482527.6165, + "updatedAt": "2026-10-06T11:06:20.884Z", + "source": "scan", + "symbols": [ + { + "name": "ExpungeRepository", + "kind": "class", + "startLine": 11, + "endLine": 13, + "tokens": 16 + }, + { + "name": "NewExpungeRepository", + "kind": "fn", + "startLine": 15, + "endLine": 17, + "tokens": 31 + }, + { + "name": "Expunge", + "kind": "method", + "startLine": 28, + "endLine": 92, + "tokens": 561 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/flag_repository_test.go": { + "description": "TestFlagRepository_SetFlags_AddThenDelThenSet", + "tokens": 1329, + "hash": "e77bc3d0befa2bec", + "size": 4650, + "mtimeMs": 1785668486702.5786, + "updatedAt": "2026-10-06T11:06:20.884Z", + "source": "scan", + "symbols": [ + { + "name": "TestFlagRepository_SetFlags_AddThenDelThenSet", + "kind": "fn", + "startLine": 12, + "endLine": 120, + "tokens": 1290 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/flag_repository.go": { + "description": "FlagRepository (45 fields); methods: SetFlags", + "tokens": 897, + "hash": "f271c91499885f83", + "size": 3139, + "mtimeMs": 1785635482527.6165, + "updatedAt": "2026-10-06T11:06:20.884Z", + "source": "scan", + "symbols": [ + { + "name": "FlagRepository", + "kind": "class", + "startLine": 13, + "endLine": 15, + "tokens": 15 + }, + { + "name": "NewFlagRepository", + "kind": "fn", + "startLine": 17, + "endLine": 19, + "tokens": 28 + }, + { + "name": "SetFlags", + "kind": "method", + "startLine": 21, + "endLine": 96, + "tokens": 796 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/imap_folder_repository_test.go": { + "description": "TestImapFolderRepository_FindByName_ReturnsFolderWithUIDData, TestImapFolderRepository_FindByName_ReturnsNilForUnknownFolder, TestImapFolderReposit...", + "tokens": 956, + "hash": "a67d0ed0472c6ee8", + "size": 3344, + "mtimeMs": 1785635482527.6165, + "updatedAt": "2026-10-06T11:06:20.884Z", + "source": "scan", + "symbols": [ + { + "name": "TestImapFolderRepository_FindByName_ReturnsFolderWithUIDData", + "kind": "fn", + "startLine": 10, + "endLine": 49, + "tokens": 412 + }, + { + "name": "TestImapFolderRepository_FindByName_ReturnsNilForUnknownFolder", + "kind": "fn", + "startLine": 51, + "endLine": 63, + "tokens": 107 + }, + { + "name": "TestImapFolderRepository_ListFolders_ReturnsAllFoldersForMailbox", + "kind": "fn", + "startLine": 65, + "endLine": 104, + "tokens": 414 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/imap_folder_repository.go": { + "description": "ImapFolderRepository (22 fields); methods: FindByName, ListFolders", + "tokens": 466, + "hash": "178fd06113d3ea1d", + "size": 1630, + "mtimeMs": 1785635482527.6165, + "updatedAt": "2026-10-06T11:06:20.884Z", + "source": "scan", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/inbound_message_repository_test.go": { + "description": "TestInboundMessageRepository_Persist_AllocatesSequentialUIDs, TestInboundMessageRepository_Persist_ReturnsErrorWhenNoInboxFolderExists, TestInbound...", + "tokens": 4003, + "hash": "77063a4e04b42da7", + "size": 14010, + "mtimeMs": 1785778216200.2603, + "updatedAt": "2026-10-06T11:06:20.884Z", + "source": "scan", + "symbols": [ + { + "name": "TestInboundMessageRepository_Persist_AllocatesSequentialUIDs", + "kind": "fn", + "startLine": 11, + "endLine": 96, + "tokens": 1000 + }, + { + "name": "TestInboundMessageRepository_Persist_ReturnsErrorWhenNoInboxFolderExists", + "kind": "fn", + "startLine": 98, + "endLine": 136, + "tokens": 464 + }, + { + "name": "TestInboundMessageRepository_PersistAll_RollsBackEveryRecipientOnFailure", + "kind": "fn", + "startLine": 141, + "endLine": 206, + "tokens": 857 + }, + { + "name": "TestInboundMessageRepository_FallsBackToInboxWhenFolderMissing", + "kind": "fn", + "startLine": 211, + "endLine": 265, + "tokens": 638 + }, + { + "name": "TestInboundMessageRepository_PersistsWithoutAuthenticationResults", + "kind": "fn", + "startLine": 275, + "endLine": 337, + "tokens": 732 + } + ], + "symbolSource": "ts", + "skeleton": "func TestInboundMessageRepository_Persist_AllocatesSequentialUIDs(t *testing.T) {\nfunc TestInboundMessageRepository_Persist_ReturnsErrorWhenNoInboxFolderExists(t *testing.T) {\nfunc TestInboundMessageRepository_PersistAll_RollsBackEveryRecipientOnFailure(t *testing.T) {\nfunc TestInboundMessageRepository_FallsBackToInboxWhenFolderMissing(t *testing.T) {\nfunc TestInboundMessageRepository_PersistsWithoutAuthenticationResults(t *testing.T) {", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/inbound_message_repository.go": { + "description": "InboundMessageRepository (55 fields); methods: PersistAll", + "tokens": 2136, + "hash": "d3ed1f96e576ea52", + "size": 7476, + "mtimeMs": 1787248947353.939, + "updatedAt": "2026-10-06T11:06:20.885Z", + "source": "scan", + "symbols": [ + { + "name": "InboundMessageRepository", + "kind": "class", + "startLine": 22, + "endLine": 24, + "tokens": 18 + }, + { + "name": "NewInboundMessageRepository", + "kind": "fn", + "startLine": 26, + "endLine": 28, + "tokens": 37 + }, + { + "name": "PersistAll", + "kind": "method", + "startLine": 33, + "endLine": 57, + "tokens": 167 + }, + { + "name": "lockFolder", + "kind": "fn", + "startLine": 61, + "endLine": 72, + "tokens": 115 + }, + { + "name": "nullIfEmpty", + "kind": "fn", + "startLine": 76, + "endLine": 81, + "tokens": 22 + }, + { + "name": "persistOne", + "kind": "fn", + "startLine": 84, + "endLine": 184, + "tokens": 1374 + }, + { + "name": "messageFlagsFor", + "kind": "fn", + "startLine": 187, + "endLine": 196, + "tokens": 75 + } + ], + "symbolSource": "ts", + "skeleton": "type InboundMessageRepository struct {\nfunc NewInboundMessageRepository(pool *pgxpool.Pool) *InboundMessageRepository {\nfunc (r *InboundMessageRepository) PersistAll(ctx context.Context, inputs []port.PersistInboundMessageInput) ([]int64, error) {\nfunc lockFolder(ctx context.Context, tx pgx.Tx, mailboxID any, folder string) (string, int64, error) {\nfunc nullIfEmpty(s string) any {\nfunc persistOne(ctx context.Context, tx pgx.Tx, input port.PersistInboundMessageInput) (int64, error) {\nfunc messageFlagsFor(input port.PersistInboundMessageInput, folder string) []string {", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/mailbox_repository_test.go": { + "description": "TestMailboxRepository_FindActiveByAddress_ReturnsNilForUnknownAddress, TestMailboxRepository_FindActiveByAddress_ReturnsRecordForSeededMailbox, Tes...", + "tokens": 3391, + "hash": "ba2c57733d50b784", + "size": 11867, + "mtimeMs": 1785668486702.8403, + "updatedAt": "2026-10-06T11:06:20.885Z", + "source": "scan", + "symbols": [ + { + "name": "testPool", + "kind": "fn", + "startLine": 16, + "endLine": 30, + "tokens": 128 + }, + { + "name": "TestMailboxRepository_FindActiveByAddress_ReturnsNilForUnknownAddress", + "kind": "fn", + "startLine": 32, + "endLine": 44, + "tokens": 123 + }, + { + "name": "TestMailboxRepository_FindActiveByAddress_ReturnsRecordForSeededMailbox", + "kind": "fn", + "startLine": 46, + "endLine": 88, + "tokens": 515 + }, + { + "name": "TestMailboxRepository_FindActiveByAddress_ExcludesInactiveMailbox", + "kind": "fn", + "startLine": 90, + "endLine": 121, + "tokens": 373 + }, + { + "name": "TestMailboxRepository_FindActiveByAddress_ReturnsQuotaAndUsedBytes", + "kind": "fn", + "startLine": 123, + "endLine": 158, + "tokens": 368 + }, + { + "name": "TestMailboxRepository_ResolveDeliveryTargets_PrefersDirectMailboxMatch", + "kind": "fn", + "startLine": 160, + "endLine": 190, + "tokens": 340 + }, + { + "name": "TestMailboxRepository_ResolveDeliveryTargets_ResolvesSpecificAlias", + "kind": "fn", + "startLine": 192, + "endLine": 229, + "tokens": 434 + }, + { + "name": "TestMailboxRepository_ResolveDeliveryTargets_ResolvesCatchAllAlias", + "kind": "fn", + "startLine": 231, + "endLine": 267, + "tokens": 442 + }, + { + "name": "TestMailboxRepository_ResolveDeliveryTargets_ReturnsEmptySliceWhenNothingMatches", + "kind": "fn", + "startLine": 269, + "endLine": 281, + "tokens": 136 + }, + { + "name": "TestMailboxRepository_ResolveDeliveryTargets_SkipsAliasDestinationThatIsNotAnInternalMailbox", + "kind": "fn", + "startLine": 283, + "endLine": 312, + "tokens": 340 + }, + { + "name": "testBlobDigest", + "kind": "fn", + "startLine": 317, + "endLine": 319, + "tokens": 33 + } + ], + "symbolSource": "ts", + "skeleton": "func testPool(t *testing.T) *pgxpool.Pool {\nfunc TestMailboxRepository_FindActiveByAddress_ReturnsNilForUnknownAddress(t *testing.T) {\nfunc TestMailboxRepository_FindActiveByAddress_ReturnsRecordForSeededMailbox(t *testing.T) {\nfunc TestMailboxRepository_FindActiveByAddress_ExcludesInactiveMailbox(t *testing.T) {\nfunc TestMailboxRepository_FindActiveByAddress_ReturnsQuotaAndUsedBytes(t *testing.T) {\nfunc TestMailboxRepository_ResolveDeliveryTargets_PrefersDirectMailboxMatch(t *testing.T) {\nfunc TestMailboxRepository_ResolveDeliveryTargets_ResolvesSpecificAlias(t *testing.T) {\nfunc TestMailboxRepository_ResolveDeliveryTargets_ResolvesCatchAllAlias(t *testing.T) {\nfunc TestMailboxRepository_ResolveDeliveryTargets_ReturnsEmptySliceWhenNothingMatches(t *testing.T) {\nfunc TestMailboxRepository_ResolveDeliveryTargets_SkipsAliasDestinationThatIsNotAnInternalMailbox(t *testing.T) {\nfunc testBlobDigest() string {", + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/mailbox_repository.go": { + "description": "MailboxRepository (33 fields); methods: FindActiveByAddress, ResolveDeliveryTargets", + "tokens": 699, + "hash": "089bb164b8331250", + "size": 2444, + "mtimeMs": 1785635482527.6165, + "updatedAt": "2026-10-06T11:06:20.885Z", + "source": "scan", + "symbols": [ + { + "name": "MailboxRepository", + "kind": "class", + "startLine": 14, + "endLine": 16, + "tokens": 16 + }, + { + "name": "NewMailboxRepository", + "kind": "fn", + "startLine": 18, + "endLine": 20, + "tokens": 31 + }, + { + "name": "FindActiveByAddress", + "kind": "method", + "startLine": 22, + "endLine": 42, + "tokens": 175 + }, + { + "name": "ResolveDeliveryTargets", + "kind": "method", + "startLine": 49, + "endLine": 88, + "tokens": 300 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/message_query_repository_test.go": { + "description": "TestMessageQueryRepository_ListMessages_ReturnsRowsOrderedByUID", + "tokens": 657, + "hash": "1cab871151fc80cf", + "size": 2299, + "mtimeMs": 1785668486702.919, + "updatedAt": "2026-10-06T11:06:20.885Z", + "source": "scan", + "symbols": [ + { + "name": "TestMessageQueryRepository_ListMessages_ReturnsRowsOrderedByUID", + "kind": "fn", + "startLine": 10, + "endLine": 67, + "tokens": 634 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/message_query_repository.go": { + "description": "MessageQueryRepository (16 fields); methods: ListMessages", + "tokens": 366, + "hash": "671b9d671bbdd297", + "size": 1281, + "mtimeMs": 1785635482527.9329, + "updatedAt": "2026-10-06T11:06:20.885Z", + "source": "scan", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/outbound_repository.go": { + "description": "OutboundRepository (41 fields); methods: Enqueue, FetchNextReady, CountRecipientsSince, UpdateJob", + "tokens": 1237, + "hash": "8565500cbea793ae", + "size": 4328, + "mtimeMs": 1785635482527.9329, + "updatedAt": "2026-10-06T11:06:20.885Z", + "source": "scan", + "symbols": [ + { + "name": "OutboundRepository", + "kind": "class", + "startLine": 15, + "endLine": 17, + "tokens": 16 + }, + { + "name": "NewOutboundRepository", + "kind": "fn", + "startLine": 19, + "endLine": 21, + "tokens": 32 + }, + { + "name": "Enqueue", + "kind": "method", + "startLine": 23, + "endLine": 37, + "tokens": 176 + }, + { + "name": "FetchNextReady", + "kind": "method", + "startLine": 39, + "endLine": 99, + "tokens": 632 + }, + { + "name": "CountRecipientsSince", + "kind": "method", + "startLine": 104, + "endLine": 115, + "tokens": 118 + }, + { + "name": "UpdateJob", + "kind": "method", + "startLine": 117, + "endLine": 130, + "tokens": 162 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/outbox_repository.go": { + "description": "OutboxRepository (18 fields); methods: FetchUnpublished, MarkPublished", + "tokens": 415, + "hash": "3c2a1a969ff8a1e8", + "size": 1450, + "mtimeMs": 1785689161519.0789, + "updatedAt": "2026-10-06T11:06:20.885Z", + "source": "scan", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/pool.go": { + "description": "contains pgx-based implementations of the application's", + "tokens": 120, + "hash": "4ac253ba740e987e", + "size": 417, + "mtimeMs": 1785635482527.9329, + "updatedAt": "2026-10-06T11:06:20.885Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/report_ingestion_test.go": { + "description": "dbBlobStore (59 fields); methods: Store", + "tokens": 3519, + "hash": "b22d5c3f4d255a49", + "size": 12316, + "mtimeMs": 1787316252903.122, + "updatedAt": "2026-10-06T11:06:20.885Z", + "source": "scan", + "symbols": [ + { + "name": "reportMail", + "kind": "fn", + "startLine": 36, + "endLine": 62, + "tokens": 291 + }, + { + "name": "TestDeliveredTlsRptReportIsIngestedAndFiled", + "kind": "fn", + "startLine": 64, + "endLine": 132, + "tokens": 678 + }, + { + "name": "dbBlobStore", + "kind": "class", + "startLine": 136, + "endLine": 142, + "tokens": 69 + }, + { + "name": "Store", + "kind": "method", + "startLine": 144, + "endLine": 167, + "tokens": 280 + }, + { + "name": "TestIngestingTheSameReportTwiceStoresItOnce", + "kind": "fn", + "startLine": 173, + "endLine": 220, + "tokens": 427 + }, + { + "name": "TestIngestingTheSameDmarcReportTwiceStoresItOnce", + "kind": "fn", + "startLine": 225, + "endLine": 263, + "tokens": 343 + }, + { + "name": "TestBackfillParsesAReportAlreadySittingInTheInbox", + "kind": "fn", + "startLine": 267, + "endLine": 355, + "tokens": 887 + } + ], + "symbolSource": "ts", + "skeleton": "func reportMail(t *testing.T, filename string, content []byte) []byte {\nfunc TestDeliveredTlsRptReportIsIngestedAndFiled(t *testing.T) {\ntype dbBlobStore struct {\nfunc (s *dbBlobStore) Store(ctx context.Context, r io.Reader) (port.BlobRef, error) {\nfunc TestIngestingTheSameReportTwiceStoresItOnce(t *testing.T) {\nfunc TestIngestingTheSameDmarcReportTwiceStoresItOnce(t *testing.T) {\nfunc TestBackfillParsesAReportAlreadySittingInTheInbox(t *testing.T) {", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/application/usecase/apply_rules_test.go", + "services/protocols/internal/domain/entity/dkim_key_pair_test.go", + "services/protocols/internal/infrastructure/diskstorage/local_disk_blob_reader_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/report_repository_test.go": { + "description": "TestReportRepository_SaveDmarcAndTlsRpt", + "tokens": 406, + "hash": "837f8c4ed3ff480a", + "size": 1419, + "mtimeMs": 1785635482527.9329, + "updatedAt": "2026-10-06T11:06:20.885Z", + "source": "scan", + "imports": [ + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/report_repository.go": { + "description": "ReportRepository (36 fields); methods: SaveDmarcReport, SaveTlsRptReport", + "tokens": 1023, + "hash": "245c95c5cdf8d384", + "size": 3580, + "mtimeMs": 1787315440759.9253, + "updatedAt": "2026-10-06T11:06:20.885Z", + "source": "scan", + "symbols": [ + { + "name": "ReportRepository", + "kind": "class", + "startLine": 13, + "endLine": 15, + "tokens": 15 + }, + { + "name": "NewReportRepository", + "kind": "fn", + "startLine": 17, + "endLine": 19, + "tokens": 30 + }, + { + "name": "SaveDmarcReport", + "kind": "method", + "startLine": 21, + "endLine": 68, + "tokens": 561 + }, + { + "name": "SaveTlsRptReport", + "kind": "method", + "startLine": 70, + "endLine": 106, + "tokens": 371 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/sieve_repository_test.go": { + "description": "TestSieveRepository_CRUD", + "tokens": 627, + "hash": "c3f02042634291da", + "size": 2193, + "mtimeMs": 1785635482527.9329, + "updatedAt": "2026-10-06T11:06:20.885Z", + "source": "scan", + "symbols": [ + { + "name": "TestSieveRepository_CRUD", + "kind": "fn", + "startLine": 11, + "endLine": 75, + "tokens": 602 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/sieve_repository.go": { + "description": "SieveRepository (55 fields); methods: GetScript, PutScript, ListScripts, SetActiveScript", + "tokens": 1245, + "hash": "f6a90ebb84378410", + "size": 4355, + "mtimeMs": 1787416024458.1255, + "updatedAt": "2026-10-06T11:06:20.886Z", + "source": "scan", + "symbols": [ + { + "name": "SieveRepository", + "kind": "class", + "startLine": 16, + "endLine": 18, + "tokens": 15 + }, + { + "name": "NewSieveRepository", + "kind": "fn", + "startLine": 20, + "endLine": 22, + "tokens": 29 + }, + { + "name": "GetScript", + "kind": "method", + "startLine": 24, + "endLine": 38, + "tokens": 156 + }, + { + "name": "PutScript", + "kind": "method", + "startLine": 40, + "endLine": 50, + "tokens": 123 + }, + { + "name": "ListScripts", + "kind": "method", + "startLine": 52, + "endLine": 73, + "tokens": 182 + }, + { + "name": "SetActiveScript", + "kind": "method", + "startLine": 75, + "endLine": 101, + "tokens": 215 + }, + { + "name": "DeleteScript", + "kind": "method", + "startLine": 103, + "endLine": 111, + "tokens": 95 + }, + { + "name": "RenameScript", + "kind": "method", + "startLine": 113, + "endLine": 121, + "tokens": 105 + }, + { + "name": "ActiveScriptFor", + "kind": "method", + "startLine": 131, + "endLine": 145, + "tokens": 126 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/vacation_log.go": { + "description": "VacationLog (16 fields); methods: LastRepliedAt, RecordReply", + "tokens": 436, + "hash": "582f1aeb9cba3432", + "size": 1525, + "mtimeMs": 1787422419526.6418, + "updatedAt": "2026-10-06T11:06:20.886Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/webmail_repository_test.go": { + "description": "TestUnreadCountFollowsWhatWasActuallyRead, TestUnreadCountSelfHealsAfterDrift, TestOwnSentCopyIsNotCountedAsUnread, TestDeleteMovesToTrashThenExpun...", + "tokens": 2027, + "hash": "12f1d7dcc134b65c", + "size": 7092, + "mtimeMs": 1787249048911.5466, + "updatedAt": "2026-10-06T11:06:20.886Z", + "source": "scan", + "symbols": [ + { + "name": "seedMailbox", + "kind": "fn", + "startLine": 20, + "endLine": 49, + "tokens": 306 + }, + { + "name": "deliver", + "kind": "fn", + "startLine": 52, + "endLine": 69, + "tokens": 194 + }, + { + "name": "storeTestBlob", + "kind": "fn", + "startLine": 71, + "endLine": 81, + "tokens": 128 + }, + { + "name": "folderByRole", + "kind": "fn", + "startLine": 83, + "endLine": 92, + "tokens": 75 + }, + { + "name": "TestUnreadCountFollowsWhatWasActuallyRead", + "kind": "fn", + "startLine": 96, + "endLine": 121, + "tokens": 242 + }, + { + "name": "TestUnreadCountSelfHealsAfterDrift", + "kind": "fn", + "startLine": 125, + "endLine": 151, + "tokens": 244 + }, + { + "name": "TestOwnSentCopyIsNotCountedAsUnread", + "kind": "fn", + "startLine": 154, + "endLine": 172, + "tokens": 173 + }, + { + "name": "TestDeleteMovesToTrashThenExpungesFromTrash", + "kind": "fn", + "startLine": 177, + "endLine": 211, + "tokens": 356 + } + ], + "symbolSource": "ts", + "skeleton": "func seedMailbox(t *testing.T, pool *pgxpool.Pool) string {\nfunc deliver(t *testing.T, pool *pgxpool.Pool, mailboxID, folder, subject string, alreadySeen bool) uint32 {\nfunc storeTestBlob(t *testing.T, pool *pgxpool.Pool) uuid.UUID {\nfunc folderByRole(t *testing.T, folders []port.WebmailFolder, role string) port.WebmailFolder {\nfunc TestUnreadCountFollowsWhatWasActuallyRead(t *testing.T) {\nfunc TestUnreadCountSelfHealsAfterDrift(t *testing.T) {\nfunc TestOwnSentCopyIsNotCountedAsUnread(t *testing.T) {\nfunc TestDeleteMovesToTrashThenExpungesFromTrash(t *testing.T) {", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/postgres/webmail_repository.go": { + "description": "WebmailRepository (145 fields); methods: FindMailboxIDByAddress, ListFolders, ListMessages, GetMessageBlob", + "tokens": 4616, + "hash": "fcb2ed5aaa27afd1", + "size": 16153, + "mtimeMs": 1787416293370.228, + "updatedAt": "2026-10-06T11:06:20.886Z", + "source": "scan", + "symbols": [ + { + "name": "WebmailRepository", + "kind": "class", + "startLine": 21, + "endLine": 23, + "tokens": 16 + }, + { + "name": "NewWebmailRepository", + "kind": "fn", + "startLine": 25, + "endLine": 27, + "tokens": 31 + }, + { + "name": "FindMailboxIDByAddress", + "kind": "method", + "startLine": 29, + "endLine": 40, + "tokens": 116 + }, + { + "name": "ListFolders", + "kind": "method", + "startLine": 42, + "endLine": 87, + "tokens": 556 + }, + { + "name": "ListMessages", + "kind": "method", + "startLine": 93, + "endLine": 142, + "tokens": 552 + }, + { + "name": "GetMessageBlob", + "kind": "method", + "startLine": 144, + "endLine": 158, + "tokens": 147 + }, + { + "name": "MarkSeen", + "kind": "method", + "startLine": 163, + "endLine": 224, + "tokens": 460 + }, + { + "name": "Expunge", + "kind": "method", + "startLine": 234, + "endLine": 286, + "tokens": 425 + }, + { + "name": "MoveToTrash", + "kind": "method", + "startLine": 300, + "endLine": 304, + "tokens": 55 + }, + { + "name": "MoveToFolder", + "kind": "method", + "startLine": 312, + "endLine": 316, + "tokens": 58 + }, + { + "name": "moveInto", + "kind": "method", + "startLine": 322, + "endLine": 408, + "tokens": 724 + }, + { + "name": "boolToInt", + "kind": "fn", + "startLine": 410, + "endLine": 415, + "tokens": 18 + }, + { + "name": "CreateFolder", + "kind": "method", + "startLine": 424, + "endLine": 432, + "tokens": 89 + }, + { + "name": "RenameFolder", + "kind": "method", + "startLine": 439, + "endLine": 451, + "tokens": 119 + }, + { + "name": "DeleteFolder", + "kind": "method", + "startLine": 458, + "endLine": 488, + "tokens": 248 + } + ], + "symbolSource": "ts", + "skeleton": "type WebmailRepository struct {\nfunc NewWebmailRepository(pool *pgxpool.Pool) *WebmailRepository {\nfunc (r *WebmailRepository) FindMailboxIDByAddress(ctx context.Context, address string) (string, error) {\nfunc (r *WebmailRepository) ListFolders(ctx context.Context, mailboxID string) ([]port.WebmailFolder, error) {\nfunc (r *WebmailRepository) ListMessages(\nfunc (r *WebmailRepository) GetMessageBlob(\nfunc (r *WebmailRepository) MarkSeen(\nfunc (r *WebmailRepository) Expunge(\nfunc (r *WebmailRepository) MoveToTrash(\nfunc (r *WebmailRepository) MoveToFolder(\nfunc (r *WebmailRepository) moveInto(\nfunc boolToInt(b bool) int {\nfunc (r *WebmailRepository) CreateFolder(ctx context.Context, mailboxID, name string) error {\nfunc (r *WebmailRepository) RenameFolder(ctx context.Context, mailboxID, from, to string) error {\nfunc (r *WebmailRepository) DeleteFolder(ctx context.Context, mailboxID, name string) error {", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/rspamd/rspamd_adapter_test.go": { + "description": "TestRspamdAdapter_Scan_MapsActions", + "tokens": 482, + "hash": "1d13ae3d06955115", + "size": 1686, + "mtimeMs": 1785635482528.4868, + "updatedAt": "2026-10-06T11:06:20.886Z", + "source": "scan", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go", + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/rspamd/rspamd_adapter.go": { + "description": "RspamdResponse (27 fields); methods: Scan", + "tokens": 882, + "hash": "265be7c3bfd0c752", + "size": 3087, + "mtimeMs": 1785635482527.9329, + "updatedAt": "2026-10-06T11:06:20.886Z", + "source": "scan", + "symbols": [ + { + "name": "RspamdResponse", + "kind": "class", + "startLine": 16, + "endLine": 21, + "tokens": 86 + }, + { + "name": "RspamdAdapter", + "kind": "class", + "startLine": 23, + "endLine": 26, + "tokens": 21 + }, + { + "name": "NewRspamdAdapter", + "kind": "fn", + "startLine": 28, + "endLine": 39, + "tokens": 118 + }, + { + "name": "Scan", + "kind": "method", + "startLine": 41, + "endLine": 108, + "tokens": 598 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go", + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/tls/acme_cert_watcher_test.go": { + "description": "domain (38 fields)", + "tokens": 3205, + "hash": "89f1682bd17c962b", + "size": 11215, + "mtimeMs": 1785668486703.125, + "updatedAt": "2026-10-06T11:06:20.886Z", + "source": "scan", + "symbols": [ + { + "name": "makeCertPEM", + "kind": "fn", + "startLine": 21, + "endLine": 24, + "tokens": 47 + }, + { + "name": "makeCertPEMWithValidity", + "kind": "fn", + "startLine": 28, + "endLine": 53, + "tokens": 256 + }, + { + "name": "writeAcmeStore", + "kind": "fn", + "startLine": 57, + "endLine": 60, + "tokens": 59 + }, + { + "name": "writeAcmeStoreWithValidity", + "kind": "fn", + "startLine": 64, + "endLine": 105, + "tokens": 358 + }, + { + "name": "TestAcmeCertWatcher_ServesCertificateBySNI", + "kind": "fn", + "startLine": 107, + "endLine": 123, + "tokens": 154 + }, + { + "name": "TestAcmeCertWatcher_HandlesAnyResolverName", + "kind": "fn", + "startLine": 127, + "endLine": 138, + "tokens": 130 + }, + { + "name": "TestAcmeCertWatcher_IndexesSubjectAlternativeNames", + "kind": "fn", + "startLine": 141, + "endLine": 154, + "tokens": 142 + }, + { + "name": "TestAcmeCertWatcher_PicksUpAtomicRename", + "kind": "fn", + "startLine": 158, + "endLine": 186, + "tokens": 286 + }, + { + "name": "TestAcmeCertWatcher_KeepsPreviousStoreOnParseFailure", + "kind": "fn", + "startLine": 190, + "endLine": 206, + "tokens": 177 + }, + { + "name": "TestAcmeCertWatcher_ReportsMissingMailHost", + "kind": "fn", + "startLine": 210, + "endLine": 219, + "tokens": 114 + }, + { + "name": "TestAcmeCertWatcher_UnknownSniFallsBackAndCounts", + "kind": "fn", + "startLine": 222, + "endLine": 235, + "tokens": 162 + }, + { + "name": "TestAcmeCertWatcher_MatchesWildcard", + "kind": "fn", + "startLine": 238, + "endLine": 247, + "tokens": 120 + }, + { + "name": "TestAcmeCertWatcher_FailsWhenStoreMissing", + "kind": "fn", + "startLine": 251, + "endLine": 255, + "tokens": 65 + }, + { + "name": "TestAcmeCertWatcher_SkipsSwapButRecordsCheckWhenContentUnchanged", + "kind": "fn", + "startLine": 261, + "endLine": 280, + "tokens": 203 + }, + { + "name": "TestAcmeCertWatcher_SkipsExpiredCertificate", + "kind": "fn", + "startLine": 284, + "endLine": 292, + "tokens": 113 + }, + { + "name": "TestAcmeCertWatcher_ReportsEarliestExpiry", + "kind": "fn", + "startLine": 296, + "endLine": 316, + "tokens": 210 + } + ], + "symbolSource": "ts", + "skeleton": "func makeCertPEM(t *testing.T, names ...string) (certPEM, keyPEM []byte) {\nfunc makeCertPEMWithValidity(t *testing.T, notBefore, notAfter time.Duration, names ...string) (certPEM, keyPEM []byte) {\nfunc writeAcmeStore(t *testing.T, dir, resolverName string, entries map[string][]string) string {\nfunc writeAcmeStoreWithValidity(t *testing.T, dir, resolverName string, entries map[string][]string, notBefore, notAfter time.Duration) string {\nfunc TestAcmeCertWatcher_ServesCertificateBySNI(t *testing.T) {\nfunc TestAcmeCertWatcher_HandlesAnyResolverName(t *testing.T) {\nfunc TestAcmeCertWatcher_IndexesSubjectAlternativeNames(t *testing.T) {\nfunc TestAcmeCertWatcher_PicksUpAtomicRename(t *testing.T) {\nfunc TestAcmeCertWatcher_KeepsPreviousStoreOnParseFailure(t *testing.T) {\nfunc TestAcmeCertWatcher_ReportsMissingMailHost(t *testing.T) {\nfunc TestAcmeCertWatcher_UnknownSniFallsBackAndCounts(t *testing.T) {\nfunc TestAcmeCertWatcher_MatchesWildcard(t *testing.T) {\nfunc TestAcmeCertWatcher_FailsWhenStoreMissing(t *testing.T) {\nfunc TestAcmeCertWatcher_SkipsSwapButRecordsCheckWhenContentUnchanged(t *testing.T) {\nfunc TestAcmeCertWatcher_SkipsExpiredCertificate(t *testing.T) {\nfunc TestAcmeCertWatcher_ReportsEarliestExpiry(t *testing.T) {", + "importance": 0.1244 + }, + "services/protocols/internal/infrastructure/tls/acme_cert_watcher.go": { + "description": "AcmeCertWatcher (73 fields); methods: Watch, EarliestExpiry, GetCertificate, LastReload", + "tokens": 2987, + "hash": "06b2810b13bbcb6d", + "size": 10454, + "mtimeMs": 1785668486703.0256, + "updatedAt": "2026-10-06T11:06:20.887Z", + "source": "scan", + "symbols": [ + { + "name": "AcmeCertWatcher", + "kind": "class", + "startLine": 33, + "endLine": 54, + "tokens": 268 + }, + { + "name": "acmeFile", + "kind": "class", + "startLine": 64, + "endLine": 73, + "tokens": 75 + }, + { + "name": "NewAcmeCertWatcher", + "kind": "fn", + "startLine": 77, + "endLine": 89, + "tokens": 94 + }, + { + "name": "Watch", + "kind": "method", + "startLine": 93, + "endLine": 113, + "tokens": 163 + }, + { + "name": "reload", + "kind": "method", + "startLine": 115, + "endLine": 144, + "tokens": 216 + }, + { + "name": "reportExpiry", + "kind": "method", + "startLine": 149, + "endLine": 163, + "tokens": 165 + }, + { + "name": "EarliestExpiry", + "kind": "method", + "startLine": 167, + "endLine": 189, + "tokens": 144 + }, + { + "name": "parseAcmeStore", + "kind": "fn", + "startLine": 194, + "endLine": 247, + "tokens": 579 + }, + { + "name": "GetCertificate", + "kind": "method", + "startLine": 252, + "endLine": 277, + "tokens": 187 + }, + { + "name": "wildcardOf", + "kind": "fn", + "startLine": 280, + "endLine": 285, + "tokens": 41 + }, + { + "name": "LastReload", + "kind": "method", + "startLine": 290, + "endLine": 295, + "tokens": 38 + }, + { + "name": "LastChange", + "kind": "method", + "startLine": 299, + "endLine": 304, + "tokens": 38 + }, + { + "name": "UnknownSNICount", + "kind": "method", + "startLine": 307, + "endLine": 309, + "tokens": 24 + }, + { + "name": "HasCertificateFor", + "kind": "method", + "startLine": 313, + "endLine": 325, + "tokens": 86 + } + ], + "symbolSource": "ts", + "skeleton": "type AcmeCertWatcher struct {\ntype acmeFile map[string]struct {\nfunc NewAcmeCertWatcher(dir, filename, defaultHost string) (*AcmeCertWatcher, error) {\nfunc (w *AcmeCertWatcher) Watch(stop <-chan struct{}, interval time.Duration) {\nfunc (w *AcmeCertWatcher) reload() error {\nfunc (w *AcmeCertWatcher) reportExpiry(now time.Time) {\nfunc (w *AcmeCertWatcher) EarliestExpiry() (string, time.Time, bool) {\nfunc parseAcmeStore(raw []byte, now time.Time) (map[string]*tls.Certificate, error) {\nfunc (w *AcmeCertWatcher) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {\nfunc wildcardOf(name string) string {\nfunc (w *AcmeCertWatcher) LastReload() time.Time {\nfunc (w *AcmeCertWatcher) LastChange() time.Time {\nfunc (w *AcmeCertWatcher) UnknownSNICount() int64 {\nfunc (w *AcmeCertWatcher) HasCertificateFor(host string) bool {", + "imports": [ + "services/protocols/internal/infrastructure/tls/acme_cert_watcher_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/tls/local_file_cert_provider_test.go": { + "description": "TestNewLocalFileCertProvider_LoadsValidCertAndKey, TestNewLocalFileCertProvider_FailsFastOnMissingFiles, TestNewEphemeralSelfSignedCertProvider_Gen...", + "tokens": 874, + "hash": "0e5d2d745b40ae2b", + "size": 3058, + "mtimeMs": 1785635482528.59, + "updatedAt": "2026-10-06T11:06:20.887Z", + "source": "scan", + "symbols": [ + { + "name": "generateSelfSignedPEMForTest", + "kind": "fn", + "startLine": 21, + "endLine": 46, + "tokens": 270 + }, + { + "name": "writeTestCertAndKey", + "kind": "fn", + "startLine": 49, + "endLine": 61, + "tokens": 130 + }, + { + "name": "TestNewLocalFileCertProvider_LoadsValidCertAndKey", + "kind": "fn", + "startLine": 63, + "endLine": 79, + "tokens": 132 + }, + { + "name": "TestNewLocalFileCertProvider_FailsFastOnMissingFiles", + "kind": "fn", + "startLine": 81, + "endLine": 86, + "tokens": 71 + }, + { + "name": "TestNewEphemeralSelfSignedCertProvider_GeneratesUsableCert", + "kind": "fn", + "startLine": 88, + "endLine": 104, + "tokens": 138 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/infrastructure/tls/acme_cert_watcher_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/tls/local_file_cert_provider.go": { + "description": "implements port.CertProvider by loading a static", + "tokens": 606, + "hash": "c7360c532a5f1b48", + "size": 2119, + "mtimeMs": 1785635482528.59, + "updatedAt": "2026-10-06T11:06:20.887Z", + "source": "scan", + "symbols": [ + { + "name": "LocalFileCertProvider", + "kind": "class", + "startLine": 20, + "endLine": 22, + "tokens": 17 + }, + { + "name": "NewLocalFileCertProvider", + "kind": "fn", + "startLine": 24, + "endLine": 30, + "tokens": 88 + }, + { + "name": "GetCertificate", + "kind": "method", + "startLine": 32, + "endLine": 34, + "tokens": 35 + }, + { + "name": "NewEphemeralSelfSignedCertProvider", + "kind": "fn", + "startLine": 40, + "endLine": 62, + "tokens": 244 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/infrastructure/tls/acme_cert_watcher_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/tls/provider_status_test.go": { + "description": "TestProviderStatus_ReportsAnEphemeralSelfSignedCertificate, TestProviderStatus_ReportsTheSpkiDigestOfTheServedCertificate", + "tokens": 846, + "hash": "6533973dc63f83be", + "size": 2959, + "mtimeMs": 1788275164223.7578, + "updatedAt": "2026-10-06T11:06:20.887Z", + "source": "scan", + "symbols": [ + { + "name": "TestProviderStatus_ReportsAnEphemeralSelfSignedCertificate", + "kind": "fn", + "startLine": 17, + "endLine": 52, + "tokens": 321 + }, + { + "name": "TestProviderStatus_ReportsTheSpkiDigestOfTheServedCertificate", + "kind": "fn", + "startLine": 58, + "endLine": 86, + "tokens": 293 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/tls/provider_status.go": { + "description": "ProviderStatus (22 fields); methods: HasCertificateFor, EarliestExpiry, LastReload, LastChange", + "tokens": 1114, + "hash": "101225f5c248c91f", + "size": 3899, + "mtimeMs": 1788275174672.6526, + "updatedAt": "2026-10-06T11:06:20.887Z", + "source": "scan", + "symbols": [ + { + "name": "ProviderStatus", + "kind": "class", + "startLine": 22, + "endLine": 24, + "tokens": 17 + }, + { + "name": "NewProviderStatus", + "kind": "fn", + "startLine": 26, + "endLine": 28, + "tokens": 33 + }, + { + "name": "leafFor", + "kind": "method", + "startLine": 31, + "endLine": 50, + "tokens": 127 + }, + { + "name": "HasCertificateFor", + "kind": "method", + "startLine": 52, + "endLine": 54, + "tokens": 28 + }, + { + "name": "EarliestExpiry", + "kind": "method", + "startLine": 58, + "endLine": 68, + "tokens": 82 + }, + { + "name": "LastReload", + "kind": "method", + "startLine": 72, + "endLine": 72, + "tokens": 20 + }, + { + "name": "LastChange", + "kind": "method", + "startLine": 73, + "endLine": 73, + "tokens": 20 + }, + { + "name": "UnknownSNICount", + "kind": "method", + "startLine": 76, + "endLine": 76, + "tokens": 18 + }, + { + "name": "CertificateSummary", + "kind": "method", + "startLine": 81, + "endLine": 98, + "tokens": 218 + }, + { + "name": "CertificateSpkiDigest", + "kind": "method", + "startLine": 106, + "endLine": 113, + "tokens": 68 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/infrastructure/tls/acme_cert_watcher_test.go", + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/tlspolicy/resolver_test.go": { + "description": "rewriteTransport (31 fields); methods: RoundTrip, LookupTLSA", + "tokens": 2177, + "hash": "fecfa7185a129c19", + "size": 7619, + "mtimeMs": 1785635482528.7688, + "updatedAt": "2026-10-06T11:06:20.887Z", + "source": "scan", + "symbols": [ + { + "name": "policyServer", + "kind": "fn", + "startLine": 19, + "endLine": 40, + "tokens": 188 + }, + { + "name": "rewriteTransport", + "kind": "class", + "startLine": 42, + "endLine": 45, + "tokens": 21 + }, + { + "name": "RoundTrip", + "kind": "method", + "startLine": 47, + "endLine": 51, + "tokens": 52 + }, + { + "name": "stubTlsaLookup", + "kind": "class", + "startLine": 53, + "endLine": 57, + "tokens": 32 + }, + { + "name": "LookupTLSA", + "kind": "method", + "startLine": 59, + "endLine": 61, + "tokens": 45 + }, + { + "name": "TestResolver_MtaStsEnforcePolicyIsParsed", + "kind": "fn", + "startLine": 63, + "endLine": 84, + "tokens": 239 + }, + { + "name": "TestResolver_MtaStsTestingModeDoesNotEnforce", + "kind": "fn", + "startLine": 86, + "endLine": 97, + "tokens": 126 + }, + { + "name": "TestResolver_MissingPolicyIsOpportunistic", + "kind": "fn", + "startLine": 101, + "endLine": 112, + "tokens": 120 + }, + { + "name": "TestResolver_CachesPolicyForMaxAge", + "kind": "fn", + "startLine": 116, + "endLine": 130, + "tokens": 142 + }, + { + "name": "TestResolver_RefetchesAfterCacheExpiry", + "kind": "fn", + "startLine": 132, + "endLine": 146, + "tokens": 166 + }, + { + "name": "TestResolver_ModeNoneIsOpportunistic", + "kind": "fn", + "startLine": 149, + "endLine": 157, + "tokens": 121 + }, + { + "name": "TestResolver_DaneWinsAndSkipsMtaStsFetch", + "kind": "fn", + "startLine": 161, + "endLine": 179, + "tokens": 210 + }, + { + "name": "TestResolver_RefusesUnauthenticatedTlsaRecords", + "kind": "fn", + "startLine": 183, + "endLine": 196, + "tokens": 153 + }, + { + "name": "TestResolver_DaneDisabledFallsBackToMtaSts", + "kind": "fn", + "startLine": 199, + "endLine": 210, + "tokens": 145 + }, + { + "name": "TestParsePolicy_ClampsAbsurdMaxAge", + "kind": "fn", + "startLine": 212, + "endLine": 222, + "tokens": 107 + } + ], + "symbolSource": "ts", + "skeleton": "func policyServer(t *testing.T, body string, status int) (*http.Client, *int) {\ntype rewriteTransport struct {\nfunc (rt rewriteTransport) RoundTrip(req *http.Request) (*http.Response, error) {\ntype stubTlsaLookup struct {\nfunc (s stubTlsaLookup) LookupTLSA(_ context.Context, _ string, _ int) ([]valueobject.TLSARecord, bool, error) {\nfunc TestResolver_MtaStsEnforcePolicyIsParsed(t *testing.T) {\nfunc TestResolver_MtaStsTestingModeDoesNotEnforce(t *testing.T) {\nfunc TestResolver_MissingPolicyIsOpportunistic(t *testing.T) {\nfunc TestResolver_CachesPolicyForMaxAge(t *testing.T) {\nfunc TestResolver_RefetchesAfterCacheExpiry(t *testing.T) {\nfunc TestResolver_ModeNoneIsOpportunistic(t *testing.T) {\nfunc TestResolver_DaneWinsAndSkipsMtaStsFetch(t *testing.T) {\nfunc TestResolver_RefusesUnauthenticatedTlsaRecords(t *testing.T) {\nfunc TestResolver_DaneDisabledFallsBackToMtaSts(t *testing.T) {\nfunc TestParsePolicy_ClampsAbsurdMaxAge(t *testing.T) {", + "imports": [ + "services/protocols/internal/infrastructure/tls/acme_cert_watcher_test.go", + "services/protocols/internal/presentation/http/admin_dkim_api.go", + "services/protocols/internal/domain/entity/dkim_key_pair_test.go", + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go" + ], + "importance": 0.015 + }, + "services/protocols/internal/infrastructure/tlspolicy/resolver.go": { + "description": "resolves the outbound transport security policy of", + "tokens": 1949, + "hash": "d9e55a3276c3793b", + "size": 6819, + "mtimeMs": 1785635482528.59, + "updatedAt": "2026-10-06T11:06:20.887Z", + "source": "scan", + "symbols": [ + { + "name": "Resolver", + "kind": "class", + "startLine": 27, + "endLine": 37, + "tokens": 54 + }, + { + "name": "TlsaLookup", + "kind": "class", + "startLine": 42, + "endLine": 44, + "tokens": 44 + }, + { + "name": "cachedPolicy", + "kind": "class", + "startLine": 46, + "endLine": 52, + "tokens": 63 + }, + { + "name": "Option", + "kind": "class", + "startLine": 54, + "endLine": 54, + "tokens": 8 + }, + { + "name": "WithDane", + "kind": "fn", + "startLine": 59, + "endLine": 64, + "tokens": 38 + }, + { + "name": "WithMtaSts", + "kind": "fn", + "startLine": 66, + "endLine": 68, + "tokens": 28 + }, + { + "name": "WithHTTPClient", + "kind": "fn", + "startLine": 70, + "endLine": 72, + "tokens": 30 + }, + { + "name": "WithClock", + "kind": "fn", + "startLine": 74, + "endLine": 76, + "tokens": 26 + }, + { + "name": "NewResolver", + "kind": "fn", + "startLine": 78, + "endLine": 89, + "tokens": 80 + }, + { + "name": "Resolve", + "kind": "method", + "startLine": 91, + "endLine": 121, + "tokens": 290 + }, + { + "name": "mtaStsPolicy", + "kind": "method", + "startLine": 123, + "endLine": 144, + "tokens": 203 + }, + { + "name": "fetchPolicy", + "kind": "method", + "startLine": 148, + "endLine": 184, + "tokens": 353 + }, + { + "name": "parsePolicy", + "kind": "fn", + "startLine": 187, + "endLine": 213, + "tokens": 217 + }, + { + "name": "cached", + "kind": "method", + "startLine": 215, + "endLine": 224, + "tokens": 66 + }, + { + "name": "store", + "kind": "method", + "startLine": 226, + "endLine": 230, + "tokens": 35 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go", + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/domain/entity/dkim_key_pair_test.go", + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/infrastructure/vault/secret_vault_test.go": { + "description": "TestSecretVault_SealOpenRoundTrip, TestSecretVault_SealIsNonDeterministic, TestSecretVault_DerivationIsStableAcrossInstances, TestSecretVault_Wrong...", + "tokens": 762, + "hash": "94aaf375f764ad47", + "size": 2664, + "mtimeMs": 1785635482528.8723, + "updatedAt": "2026-10-06T11:06:20.888Z", + "source": "scan", + "symbols": [ + { + "name": "TestSecretVault_SealOpenRoundTrip", + "kind": "fn", + "startLine": 9, + "endLine": 31, + "tokens": 183 + }, + { + "name": "TestSecretVault_SealIsNonDeterministic", + "kind": "fn", + "startLine": 35, + "endLine": 44, + "tokens": 84 + }, + { + "name": "TestSecretVault_DerivationIsStableAcrossInstances", + "kind": "fn", + "startLine": 48, + "endLine": 60, + "tokens": 132 + }, + { + "name": "TestSecretVault_WrongMasterKeyFails", + "kind": "fn", + "startLine": 62, + "endLine": 70, + "tokens": 99 + }, + { + "name": "TestSecretVault_RequiresMasterKey", + "kind": "fn", + "startLine": 74, + "endLine": 78, + "tokens": 51 + }, + { + "name": "TestSecretVault_RejectsUnknownKeyVersion", + "kind": "fn", + "startLine": 80, + "endLine": 87, + "tokens": 85 + } + ], + "symbolSource": "ts", + "importance": 0.1298 + }, + "services/protocols/internal/infrastructure/vault/secret_vault.go": { + "description": "seals the reusable secrets the system stores in Postgres -", + "tokens": 998, + "hash": "396796e974d3eb32", + "size": 3491, + "mtimeMs": 1785635482528.7688, + "updatedAt": "2026-10-06T11:06:20.888Z", + "source": "scan", + "symbols": [ + { + "name": "SecretVault", + "kind": "class", + "startLine": 38, + "endLine": 40, + "tokens": 13 + }, + { + "name": "New", + "kind": "fn", + "startLine": 45, + "endLine": 69, + "tokens": 219 + }, + { + "name": "Seal", + "kind": "method", + "startLine": 74, + "endLine": 80, + "tokens": 88 + }, + { + "name": "Open", + "kind": "method", + "startLine": 85, + "endLine": 97, + "tokens": 153 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/presentation/http/admin_dkim_api.go": { + "description": "adminDkimAPI (33 fields); methods: SetAdminDkimAPI", + "tokens": 1675, + "hash": "b971e3522a55d126", + "size": 5860, + "mtimeMs": 1785689161519.2258, + "updatedAt": "2026-10-06T11:06:20.888Z", + "source": "scan", + "symbols": [ + { + "name": "KeyGenerator", + "kind": "class", + "startLine": 20, + "endLine": 20, + "tokens": 28 + }, + { + "name": "adminDkimAPI", + "kind": "class", + "startLine": 28, + "endLine": 32, + "tokens": 34 + }, + { + "name": "bearerOrCookie", + "kind": "fn", + "startLine": 35, + "endLine": 43, + "tokens": 83 + }, + { + "name": "authenticate", + "kind": "method", + "startLine": 45, + "endLine": 57, + "tokens": 125 + }, + { + "name": "handleKeys", + "kind": "method", + "startLine": 59, + "endLine": 76, + "tokens": 136 + }, + { + "name": "handleRotate", + "kind": "method", + "startLine": 78, + "endLine": 132, + "tokens": 548 + }, + { + "name": "dkimPublicRecord", + "kind": "fn", + "startLine": 135, + "endLine": 141, + "tokens": 57 + }, + { + "name": "isValidSelector", + "kind": "fn", + "startLine": 143, + "endLine": 155, + "tokens": 80 + }, + { + "name": "SetAdminDkimAPI", + "kind": "method", + "startLine": 159, + "endLine": 164, + "tokens": 83 + }, + { + "name": "handleAdminDkimKeys", + "kind": "method", + "startLine": 166, + "endLine": 173, + "tokens": 76 + }, + { + "name": "handleAdminDkimRotate", + "kind": "method", + "startLine": 175, + "endLine": 182, + "tokens": 77 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/infrastructure/postgres/acme_repository.go" + ], + "importance": 0.1889 + }, + "services/protocols/internal/presentation/http/admin_dns_api.go": { + "description": "Interface: DnsRecordVerifier (21 methods)", + "tokens": 2650, + "hash": "02f0b304b7e9f4fd", + "size": 9274, + "mtimeMs": 1787571927240.7104, + "updatedAt": "2026-10-06T11:06:20.888Z", + "source": "scan", + "symbols": [ + { + "name": "DnsRecordVerifier", + "kind": "class", + "startLine": 15, + "endLine": 17, + "tokens": 32 + }, + { + "name": "DnsSpecSource", + "kind": "class", + "startLine": 22, + "endLine": 24, + "tokens": 33 + }, + { + "name": "ReconcileResult", + "kind": "class", + "startLine": 31, + "endLine": 38, + "tokens": 73 + }, + { + "name": "DomainReconciler", + "kind": "class", + "startLine": 46, + "endLine": 48, + "tokens": 33 + }, + { + "name": "DnsStatusWriter", + "kind": "class", + "startLine": 55, + "endLine": 57, + "tokens": 29 + }, + { + "name": "adminDnsAPI", + "kind": "class", + "startLine": 59, + "endLine": 69, + "tokens": 114 + }, + { + "name": "handleReconcile", + "kind": "method", + "startLine": 76, + "endLine": 111, + "tokens": 388 + }, + { + "name": "handleVerify", + "kind": "method", + "startLine": 121, + "endLine": 199, + "tokens": 659 + }, + { + "name": "SetAdminDnsAPI", + "kind": "method", + "startLine": 203, + "endLine": 215, + "tokens": 150 + }, + { + "name": "handleAdminDnsReconcile", + "kind": "method", + "startLine": 218, + "endLine": 229, + "tokens": 118 + }, + { + "name": "SetDnsStatusWriter", + "kind": "method", + "startLine": 234, + "endLine": 239, + "tokens": 34 + }, + { + "name": "SetDnsReconciler", + "kind": "method", + "startLine": 241, + "endLine": 251, + "tokens": 148 + }, + { + "name": "handleAdminDnsVerify", + "kind": "method", + "startLine": 253, + "endLine": 260, + "tokens": 78 + } + ], + "symbolSource": "ts", + "skeleton": "type DnsRecordVerifier interface {\ntype DnsSpecSource interface {\ntype ReconcileResult struct {\ntype DomainReconciler interface {\ntype DnsStatusWriter interface {\ntype adminDnsAPI struct {\nfunc (a *adminDnsAPI) handleReconcile(w http.ResponseWriter, r *http.Request) {\nfunc (a *adminDnsAPI) handleVerify(w http.ResponseWriter, r *http.Request) {\nfunc (r *Router) SetAdminDnsAPI(spec DnsSpecSource, verifier DnsRecordVerifier, sessionSecret string) {\nfunc (r *Router) handleAdminDnsReconcile(w http.ResponseWriter, req *http.Request) {\nfunc (r *Router) SetDnsStatusWriter(w DnsStatusWriter) {\nfunc (r *Router) SetDnsReconciler(reconciler DomainReconciler) {\nfunc (r *Router) handleAdminDnsVerify(w http.ResponseWriter, req *http.Request) {", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go", + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/presentation/http/admin_dns_reconcile_test.go": { + "description": "stubReconciler (42 fields); methods: ReconcileDomain, SaveDnsStatus", + "tokens": 2445, + "hash": "aa1bdab857fdb9af", + "size": 8555, + "mtimeMs": 1787571912654.9138, + "updatedAt": "2026-10-06T11:06:20.888Z", + "source": "scan", + "symbols": [ + { + "name": "stubReconciler", + "kind": "class", + "startLine": 24, + "endLine": 28, + "tokens": 22 + }, + { + "name": "ReconcileDomain", + "kind": "method", + "startLine": 30, + "endLine": 36, + "tokens": 75 + }, + { + "name": "newReconcileAPI", + "kind": "fn", + "startLine": 38, + "endLine": 41, + "tokens": 45 + }, + { + "name": "adminRequest", + "kind": "fn", + "startLine": 43, + "endLine": 47, + "tokens": 59 + }, + { + "name": "TestReconcileActuallyReconcilesTheDomain", + "kind": "fn", + "startLine": 49, + "endLine": 69, + "tokens": 232 + }, + { + "name": "TestReconcileRefusesWithoutAnAdminSession", + "kind": "fn", + "startLine": 71, + "endLine": 83, + "tokens": 134 + }, + { + "name": "TestReconcileRefusesAUserSession", + "kind": "fn", + "startLine": 86, + "endLine": 99, + "tokens": 148 + }, + { + "name": "TestReconcileRequiresADomain", + "kind": "fn", + "startLine": 101, + "endLine": 112, + "tokens": 110 + }, + { + "name": "TestReconcileReportsAProviderFailure", + "kind": "fn", + "startLine": 116, + "endLine": 129, + "tokens": 172 + }, + { + "name": "TestReconcileSaysWhenItIsNotConfigured", + "kind": "fn", + "startLine": 132, + "endLine": 140, + "tokens": 104 + }, + { + "name": "adminSurfaceToken2", + "kind": "fn", + "startLine": 142, + "endLine": 149, + "tokens": 86 + }, + { + "name": "stubSpec", + "kind": "class", + "startLine": 153, + "endLine": 153, + "tokens": 7 + }, + { + "name": "ExpectedRecords", + "kind": "method", + "startLine": 155, + "endLine": 157, + "tokens": 30 + }, + { + "name": "stubVerifier", + "kind": "class", + "startLine": 159, + "endLine": 159, + "tokens": 8 + }, + { + "name": "VerifyRecord", + "kind": "method", + "startLine": 161, + "endLine": 163, + "tokens": 30 + }, + { + "name": "TestReconcilerSurvivesBeingWiredBeforeTheDnsAPI", + "kind": "fn", + "startLine": 173, + "endLine": 206, + "tokens": 355 + }, + { + "name": "stubStatusWriter", + "kind": "class", + "startLine": 208, + "endLine": 212, + "tokens": 22 + }, + { + "name": "SaveDnsStatus", + "kind": "method", + "startLine": 214, + "endLine": 218, + "tokens": 44 + }, + { + "name": "TestVerifyRecordsWhatItFound", + "kind": "fn", + "startLine": 226, + "endLine": 247, + "tokens": 218 + } + ], + "symbolSource": "ts", + "skeleton": "type stubReconciler struct {\nfunc (s *stubReconciler) ReconcileDomain(_ context.Context, domain string) (ReconcileResult, error) {\nfunc newReconcileAPI(t *testing.T, r *stubReconciler) *adminDnsAPI {\nfunc adminRequest(target string) *http.Request {\nfunc TestReconcileActuallyReconcilesTheDomain(t *testing.T) {\nfunc TestReconcileRefusesWithoutAnAdminSession(t *testing.T) {\nfunc TestReconcileRefusesAUserSession(t *testing.T) {\nfunc TestReconcileRequiresADomain(t *testing.T) {\nfunc TestReconcileReportsAProviderFailure(t *testing.T) {\nfunc TestReconcileSaysWhenItIsNotConfigured(t *testing.T) {\nfunc adminSurfaceToken2() string {\ntype stubSpec struct{}\nfunc (stubSpec) ExpectedRecords(context.Context, string) ([]entity.DnsRecord, error) {\ntype stubVerifier struct{}\nfunc (stubVerifier) VerifyRecord(context.Context, entity.DnsRecord) (bool, string) {\nfunc TestReconcilerSurvivesBeingWiredBeforeTheDnsAPI(t *testing.T) {\ntype stubStatusWriter struct {\nfunc (s *stubStatusWriter) SaveDnsStatus(_ context.Context, domain, status string) error {\nfunc TestVerifyRecordsWhatItFound(t *testing.T) {", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go", + "services/protocols/internal/domain/entity/dkim_key_pair_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/presentation/http/admin_tls_api_test.go": { + "description": "TestAcmeCertWatcherSatisfiesTlsStatusSource", + "tokens": 155, + "hash": "673036cbb456c177", + "size": 541, + "mtimeMs": 1787583336687.2886, + "updatedAt": "2026-10-06T11:06:20.888Z", + "source": "scan", + "imports": [ + "services/protocols/internal/infrastructure/tls/acme_cert_watcher_test.go", + "services/protocols/internal/presentation/http/admin_dkim_api.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/presentation/http/admin_tls_api.go": { + "description": "Interface: TlsStatusSource (14 methods)", + "tokens": 1376, + "hash": "9015a060030e2704", + "size": 4816, + "mtimeMs": 1788275187056.9775, + "updatedAt": "2026-10-06T11:06:20.889Z", + "source": "scan", + "symbols": [ + { + "name": "TlsStatusSource", + "kind": "class", + "startLine": 11, + "endLine": 17, + "tokens": 58 + }, + { + "name": "TlsCertificateInspector", + "kind": "class", + "startLine": 22, + "endLine": 24, + "tokens": 34 + }, + { + "name": "TlsDaneInspector", + "kind": "class", + "startLine": 28, + "endLine": 30, + "tokens": 25 + }, + { + "name": "adminTlsAPI", + "kind": "class", + "startLine": 32, + "endLine": 38, + "tokens": 44 + }, + { + "name": "handleStatus", + "kind": "method", + "startLine": 40, + "endLine": 122, + "tokens": 841 + }, + { + "name": "SetAdminTlsAPI", + "kind": "method", + "startLine": 125, + "endLine": 136, + "tokens": 112 + }, + { + "name": "handleAdminTls", + "kind": "method", + "startLine": 138, + "endLine": 145, + "tokens": 76 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/presentation/http/admin_tls_status_test.go": { + "description": "stillSource (4 fields)", + "tokens": 513, + "hash": "a333c21f54280560", + "size": 1795, + "mtimeMs": 1787583336694.784, + "updatedAt": "2026-10-06T11:06:20.889Z", + "source": "scan", + "symbols": [ + { + "name": "TestTlsStatus_NoWatcherVerdictWithoutAReloadTime", + "kind": "fn", + "startLine": 15, + "endLine": 41, + "tokens": 254 + }, + { + "name": "stillSource", + "kind": "class", + "startLine": 45, + "endLine": 45, + "tokens": 8 + }, + { + "name": "EarliestExpiry", + "kind": "method", + "startLine": 47, + "endLine": 49, + "tokens": 39 + }, + { + "name": "LastReload", + "kind": "method", + "startLine": 50, + "endLine": 50, + "tokens": 21 + }, + { + "name": "LastChange", + "kind": "method", + "startLine": 51, + "endLine": 51, + "tokens": 21 + }, + { + "name": "UnknownSNICount", + "kind": "method", + "startLine": 52, + "endLine": 52, + "tokens": 18 + }, + { + "name": "HasCertificateFor", + "kind": "method", + "startLine": 53, + "endLine": 53, + "tokens": 19 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/presentation/http/attachment_test.go": { + "description": "TestAttachmentDownload_DowngradesRenderableContentTypes, TestAttachmentDownload_KeepsHarmlessContentTypes, TestAttachmentDownload_NeutralisesHostil...", + "tokens": 937, + "hash": "e0c7e15db1a21af1", + "size": 3278, + "mtimeMs": 1785689161519.3965, + "updatedAt": "2026-10-06T11:06:20.889Z", + "source": "scan", + "symbols": [ + { + "name": "buildMessage", + "kind": "fn", + "startLine": 13, + "endLine": 20, + "tokens": 115 + }, + { + "name": "download", + "kind": "fn", + "startLine": 22, + "endLine": 27, + "tokens": 71 + }, + { + "name": "TestAttachmentDownload_DowngradesRenderableContentTypes", + "kind": "fn", + "startLine": 31, + "endLine": 38, + "tokens": 122 + }, + { + "name": "TestAttachmentDownload_KeepsHarmlessContentTypes", + "kind": "fn", + "startLine": 40, + "endLine": 45, + "tokens": 76 + }, + { + "name": "TestAttachmentDownload_NeutralisesHostileFilenames", + "kind": "fn", + "startLine": 49, + "endLine": 64, + "tokens": 192 + }, + { + "name": "TestAttachmentDownload_SetsSniffingAndSandboxGuards", + "kind": "fn", + "startLine": 66, + "endLine": 74, + "tokens": 126 + }, + { + "name": "TestAttachmentDownload_MissingAttachmentIs404", + "kind": "fn", + "startLine": 76, + "endLine": 81, + "tokens": 72 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/presentation/http/bimi_api.go": { + "description": "Interface: BimiStore (24 methods)", + "tokens": 2025, + "hash": "477b5e448bce585d", + "size": 7087, + "mtimeMs": 1788830725028.3633, + "updatedAt": "2026-10-06T11:06:20.889Z", + "source": "scan", + "symbols": [ + { + "name": "BimiStore", + "kind": "class", + "startLine": 16, + "endLine": 20, + "tokens": 64 + }, + { + "name": "BimiLogo", + "kind": "class", + "startLine": 23, + "endLine": 27, + "tokens": 20 + }, + { + "name": "bimiAPI", + "kind": "class", + "startLine": 29, + "endLine": 35, + "tokens": 66 + }, + { + "name": "SetBimiAPI", + "kind": "method", + "startLine": 38, + "endLine": 46, + "tokens": 76 + }, + { + "name": "handleBimiLogo", + "kind": "method", + "startLine": 54, + "endLine": 92, + "tokens": 395 + }, + { + "name": "domainFor", + "kind": "method", + "startLine": 94, + "endLine": 102, + "tokens": 100 + }, + { + "name": "handleBimiAdmin", + "kind": "method", + "startLine": 105, + "endLine": 143, + "tokens": 378 + }, + { + "name": "get", + "kind": "method", + "startLine": 145, + "endLine": 162, + "tokens": 155 + }, + { + "name": "put", + "kind": "method", + "startLine": 164, + "endLine": 212, + "tokens": 528 + }, + { + "name": "BimiLogoURL", + "kind": "fn", + "startLine": 215, + "endLine": 217, + "tokens": 34 + } + ], + "symbolSource": "ts", + "skeleton": "type BimiStore interface {\ntype BimiLogo struct {\ntype bimiAPI struct {\nfunc (r *Router) SetBimiAPI(store BimiStore, sessionSecret, defaultDomain string) {\nfunc (r *Router) handleBimiLogo(w http.ResponseWriter, req *http.Request) {\nfunc (a *bimiAPI) domainFor(req *http.Request) string {\nfunc (r *Router) handleBimiAdmin(w http.ResponseWriter, req *http.Request) {\nfunc (a *bimiAPI) get(w http.ResponseWriter, req *http.Request, domain string) {\nfunc (a *bimiAPI) put(w http.ResponseWriter, req *http.Request, domain string) {\nfunc BimiLogoURL(mailHost string) string {", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go", + "services/protocols/internal/domain/bimi/svg_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/presentation/http/events_ws_test.go": { + "description": "TestEventsReachOnlyTheirOwnMailbox, TestASlowSubscriberIsDroppedRatherThanBlocking, TestSubscriberCountFollowsAddAndRemove, TestPublishWithNoSubscr...", + "tokens": 1693, + "hash": "c4cb652fb2b38492", + "size": 5924, + "mtimeMs": 1787316516588.1565, + "updatedAt": "2026-10-06T11:06:20.889Z", + "source": "scan", + "symbols": [ + { + "name": "newTestHub", + "kind": "fn", + "startLine": 20, + "endLine": 24, + "tokens": 63 + }, + { + "name": "subscribeTo", + "kind": "fn", + "startLine": 36, + "endLine": 40, + "tokens": 54 + }, + { + "name": "TestEventsReachOnlyTheirOwnMailbox", + "kind": "fn", + "startLine": 43, + "endLine": 68, + "tokens": 180 + }, + { + "name": "TestASlowSubscriberIsDroppedRatherThanBlocking", + "kind": "fn", + "startLine": 72, + "endLine": 93, + "tokens": 172 + }, + { + "name": "TestSubscriberCountFollowsAddAndRemove", + "kind": "fn", + "startLine": 95, + "endLine": 110, + "tokens": 133 + }, + { + "name": "TestPublishWithNoSubscribersIsHarmless", + "kind": "fn", + "startLine": 114, + "endLine": 121, + "tokens": 64 + }, + { + "name": "TestEventStreamRefusesAnUnauthenticatedUpgrade", + "kind": "fn", + "startLine": 127, + "endLine": 136, + "tokens": 97 + }, + { + "name": "TestEventStreamRefusesAnAdminSession", + "kind": "fn", + "startLine": 138, + "endLine": 149, + "tokens": 121 + }, + { + "name": "TestEventStreamRefusesASessionWithNoMailbox", + "kind": "fn", + "startLine": 153, + "endLine": 166, + "tokens": 137 + }, + { + "name": "TestEventCarriesItsPayloadThrough", + "kind": "fn", + "startLine": 170, + "endLine": 191, + "tokens": 188 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go", + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/application/usecase/apply_rules_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/presentation/http/events_ws.go": { + "description": "EventMessage (30 fields); methods: Publish, SubscriberCount, ServeHTTP, SetEventHub", + "tokens": 1490, + "hash": "df616789e6f9b313", + "size": 5213, + "mtimeMs": 1787316530269.0073, + "updatedAt": "2026-10-06T11:06:20.890Z", + "source": "scan", + "symbols": [ + { + "name": "EventMessage", + "kind": "class", + "startLine": 24, + "endLine": 28, + "tokens": 46 + }, + { + "name": "subscriber", + "kind": "class", + "startLine": 30, + "endLine": 33, + "tokens": 21 + }, + { + "name": "EventHub", + "kind": "class", + "startLine": 40, + "endLine": 45, + "tokens": 46 + }, + { + "name": "NewEventHub", + "kind": "fn", + "startLine": 47, + "endLine": 53, + "tokens": 61 + }, + { + "name": "Publish", + "kind": "method", + "startLine": 57, + "endLine": 77, + "tokens": 143 + }, + { + "name": "add", + "kind": "method", + "startLine": 79, + "endLine": 83, + "tokens": 32 + }, + { + "name": "remove", + "kind": "method", + "startLine": 85, + "endLine": 90, + "tokens": 37 + }, + { + "name": "SubscriberCount", + "kind": "method", + "startLine": 93, + "endLine": 97, + "tokens": 31 + }, + { + "name": "ServeHTTP", + "kind": "method", + "startLine": 100, + "endLine": 168, + "tokens": 533 + }, + { + "name": "SetEventHub", + "kind": "method", + "startLine": 171, + "endLine": 173, + "tokens": 18 + }, + { + "name": "handleEvents", + "kind": "method", + "startLine": 175, + "endLine": 182, + "tokens": 78 + }, + { + "name": "logDroppedSubscriber", + "kind": "fn", + "startLine": 189, + "endLine": 191, + "tokens": 39 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go", + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/application/usecase/apply_rules_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/presentation/http/mail_api_test.go": { + "description": "fakeMailStore (58 fields); methods: FindMailboxIDByAddress, ListFolders, ListMessages, GetMessageBlob", + "tokens": 5364, + "hash": "97760fa274c09922", + "size": 18773, + "mtimeMs": 1787415040915.983, + "updatedAt": "2026-10-06T11:06:20.890Z", + "source": "scan", + "symbols": [ + { + "name": "fakeMailStore", + "kind": "class", + "startLine": 31, + "endLine": 42, + "tokens": 94 + }, + { + "name": "FindMailboxIDByAddress", + "kind": "method", + "startLine": 44, + "endLine": 50, + "tokens": 58 + }, + { + "name": "ListFolders", + "kind": "method", + "startLine": 52, + "endLine": 57, + "tokens": 68 + }, + { + "name": "ListMessages", + "kind": "method", + "startLine": 59, + "endLine": 63, + "tokens": 47 + }, + { + "name": "GetMessageBlob", + "kind": "method", + "startLine": 65, + "endLine": 76, + "tokens": 114 + }, + { + "name": "MarkSeen", + "kind": "method", + "startLine": 78, + "endLine": 81, + "tokens": 52 + }, + { + "name": "Expunge", + "kind": "method", + "startLine": 83, + "endLine": 90, + "tokens": 70 + }, + { + "name": "MoveToFolder", + "kind": "method", + "startLine": 92, + "endLine": 100, + "tokens": 77 + }, + { + "name": "MoveToTrash", + "kind": "method", + "startLine": 102, + "endLine": 110, + "tokens": 69 + }, + { + "name": "fakeBlobReader", + "kind": "class", + "startLine": 112, + "endLine": 112, + "tokens": 12 + }, + { + "name": "ReadByID", + "kind": "method", + "startLine": 114, + "endLine": 116, + "tokens": 29 + }, + { + "name": "newMailAPI", + "kind": "fn", + "startLine": 122, + "endLine": 126, + "tokens": 73 + }, + { + "name": "request", + "kind": "fn", + "startLine": 131, + "endLine": 136, + "tokens": 74 + }, + { + "name": "storeWithMail", + "kind": "fn", + "startLine": 138, + "endLine": 147, + "tokens": 96 + }, + { + "name": "TestMailApiRefusesARequestWithNoSession", + "kind": "fn", + "startLine": 151, + "endLine": 175, + "tokens": 258 + }, + { + "name": "TestMailApiRefusesAnAdminSession", + "kind": "fn", + "startLine": 179, + "endLine": 190, + "tokens": 117 + }, + { + "name": "TestMailApiRefusesAnMfaChallengeToken", + "kind": "fn", + "startLine": 194, + "endLine": 205, + "tokens": 121 + }, + { + "name": "TestMailApiRefusesATamperedToken", + "kind": "fn", + "startLine": 208, + "endLine": 219, + "tokens": 127 + }, + { + "name": "TestMailApiAcceptsTheSessionCookie", + "kind": "fn", + "startLine": 222, + "endLine": 233, + "tokens": 118 + }, + { + "name": "TestMailApiServesOnlyTheSessionsOwnMailbox", + "kind": "fn", + "startLine": 239, + "endLine": 258, + "tokens": 173 + }, + { + "name": "TestMailApiCannotReadAnotherMailboxesMessageByUid", + "kind": "fn", + "startLine": 261, + "endLine": 275, + "tokens": 156 + }, + { + "name": "TestOpeningAMessageMarksItRead", + "kind": "fn", + "startLine": 280, + "endLine": 293, + "tokens": 148 + }, + { + "name": "TestMessageListDefaultsToTheInbox", + "kind": "fn", + "startLine": 296, + "endLine": 309, + "tokens": 132 + }, + { + "name": "TestDeleteMovesToTrashFromAnOrdinaryFolder", + "kind": "fn", + "startLine": 313, + "endLine": 331, + "tokens": 222 + }, + { + "name": "TestDeleteFromTrashExpungesForGood", + "kind": "fn", + "startLine": 333, + "endLine": 347, + "tokens": 160 + }, + { + "name": "TestDeleteRejectsAMissingUid", + "kind": "fn", + "startLine": 350, + "endLine": 359, + "tokens": 95 + }, + { + "name": "TestDeleteRejectsMalformedJson", + "kind": "fn", + "startLine": 361, + "endLine": 370, + "tokens": 92 + }, + { + "name": "TestDomainErrorsMapToUsefulStatusCodes", + "kind": "fn", + "startLine": 377, + "endLine": 403, + "tokens": 274 + }, + { + "name": "adminSurfaceToken", + "kind": "fn", + "startLine": 407, + "endLine": 416, + "tokens": 91 + }, + { + "name": "TestComposeAlwaysSendsAsTheSessionsOwnAddress", + "kind": "fn", + "startLine": 425, + "endLine": 443, + "tokens": 159 + } + ], + "symbolSource": "ts", + "skeleton": "type fakeMailStore struct {\nfunc (f *fakeMailStore) FindMailboxIDByAddress(_ context.Context, address string) (string, error) {\nfunc (f *fakeMailStore) ListFolders(_ context.Context, mailboxID string) ([]port.WebmailFolder, error) {\nfunc (f *fakeMailStore) ListMessages(\nfunc (f *fakeMailStore) GetMessageBlob(\nfunc (f *fakeMailStore) MarkSeen(_ context.Context, mailboxID, folder string, uid uint32, seen bool) error {\nfunc (f *fakeMailStore) Expunge(_ context.Context, mailboxID, folder string, uid uint32) error {\nfunc (f *fakeMailStore) MoveToFolder(\nfunc (f *fakeMailStore) MoveToTrash(\ntype fakeBlobReader struct{ raw []byte }\nfunc (f *fakeBlobReader) ReadByID(context.Context, uuid.UUID) ([]byte, error) {\nfunc newMailAPI(t *testing.T, store *fakeMailStore, raw []byte) *mailAPI {\nfunc request(method, target string, body string) *http.Request {\nfunc storeWithMail() *fakeMailStore {\nfunc TestMailApiRefusesARequestWithNoSession(t *testing.T) {\nfunc TestMailApiRefusesAnAdminSession(t *testing.T) {\nfunc TestMailApiRefusesAnMfaChallengeToken(t *testing.T) {\nfunc TestMailApiRefusesATamperedToken(t *testing.T) {\nfunc TestMailApiAcceptsTheSessionCookie(t *testing.T) {\nfunc TestMailApiServesOnlyTheSessionsOwnMailbox(t *testing.T) {\nfunc TestMailApiCannotReadAnotherMailboxesMessageByUid(t *testing.T) {\nfunc TestOpeningAMessageMarksItRead(t *testing.T) {", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go", + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/application/usecase/apply_rules_test.go", + "services/protocols/internal/infrastructure/postgres/acme_repository.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/presentation/http/mail_api.go": { + "description": "HTTP handlers: writeJSON, writeError", + "tokens": 4631, + "hash": "806f5f98bffb4b28", + "size": 16207, + "mtimeMs": 1787416316724.0115, + "updatedAt": "2026-10-06T11:06:20.890Z", + "source": "scan", + "symbols": [ + { + "name": "mailAPI", + "kind": "class", + "startLine": 26, + "endLine": 32, + "tokens": 74 + }, + { + "name": "writeJSON", + "kind": "fn", + "startLine": 34, + "endLine": 38, + "tokens": 52 + }, + { + "name": "writeError", + "kind": "fn", + "startLine": 40, + "endLine": 42, + "tokens": 44 + }, + { + "name": "authenticate", + "kind": "method", + "startLine": 46, + "endLine": 68, + "tokens": 234 + }, + { + "name": "handleFolders", + "kind": "method", + "startLine": 70, + "endLine": 81, + "tokens": 82 + }, + { + "name": "handleMessages", + "kind": "method", + "startLine": 83, + "endLine": 103, + "tokens": 149 + }, + { + "name": "handleMessage", + "kind": "method", + "startLine": 105, + "endLine": 145, + "tokens": 341 + }, + { + "name": "handleAttachmentDownload", + "kind": "method", + "startLine": 147, + "endLine": 174, + "tokens": 369 + }, + { + "name": "safeAttachmentContentType", + "kind": "fn", + "startLine": 192, + "endLine": 198, + "tokens": 64 + }, + { + "name": "sanitiseFilename", + "kind": "fn", + "startLine": 202, + "endLine": 220, + "tokens": 113 + }, + { + "name": "handleSeen", + "kind": "method", + "startLine": 222, + "endLine": 246, + "tokens": 190 + }, + { + "name": "handleSend", + "kind": "method", + "startLine": 248, + "endLine": 264, + "tokens": 104 + }, + { + "name": "handleDelete", + "kind": "method", + "startLine": 268, + "endLine": 295, + "tokens": 212 + }, + { + "name": "handleFolderAdmin", + "kind": "method", + "startLine": 299, + "endLine": 338, + "tokens": 320 + }, + { + "name": "handleMove", + "kind": "method", + "startLine": 341, + "endLine": 369, + "tokens": 224 + }, + { + "name": "handleSaveDraft", + "kind": "method", + "startLine": 372, + "endLine": 389, + "tokens": 138 + }, + { + "name": "decodeCompose", + "kind": "method", + "startLine": 397, + "endLine": 438, + "tokens": 411 + }, + { + "name": "fail", + "kind": "method", + "startLine": 441, + "endLine": 472, + "tokens": 577 + }, + { + "name": "decodeDraft", + "kind": "method", + "startLine": 479, + "endLine": 498, + "tokens": 199 + } + ], + "symbolSource": "ts", + "skeleton": "type mailAPI struct {\nfunc writeJSON(w http.ResponseWriter, status int, payload any) {\nfunc writeError(w http.ResponseWriter, status int, code, message string) {\nfunc (m *mailAPI) authenticate(w http.ResponseWriter, r *http.Request) (*WebSession, bool) {\nfunc (m *mailAPI) handleFolders(w http.ResponseWriter, r *http.Request) {\nfunc (m *mailAPI) handleMessages(w http.ResponseWriter, r *http.Request) {\nfunc (m *mailAPI) handleMessage(w http.ResponseWriter, r *http.Request) {\nfunc (m *mailAPI) handleAttachmentDownload(w http.ResponseWriter, _ *http.Request, raw []byte, filename string) {\nfunc safeAttachmentContentType(declared string) string {\nfunc sanitiseFilename(name string) string {\nfunc (m *mailAPI) handleSeen(w http.ResponseWriter, r *http.Request) {\nfunc (m *mailAPI) handleSend(w http.ResponseWriter, r *http.Request) {\nfunc (m *mailAPI) handleDelete(w http.ResponseWriter, r *http.Request) {\nfunc (m *mailAPI) handleFolderAdmin(w http.ResponseWriter, r *http.Request) {\nfunc (m *mailAPI) handleMove(w http.ResponseWriter, r *http.Request) {\nfunc (m *mailAPI) handleSaveDraft(w http.ResponseWriter, r *http.Request) {\nfunc (m *mailAPI) decodeCompose(\nfunc (m *mailAPI) fail(w http.ResponseWriter, err error) {\nfunc (m *mailAPI) decodeDraft(", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go", + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/application/usecase/apply_rules_test.go", + "services/protocols/internal/infrastructure/postgres/acme_repository.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/presentation/http/router_test.go": { + "description": "TestRouter_MtaSts_Autoconfig_SecurityTxt_Ingest, TestRouter_HealthReportsDegradedWithoutFailing, TestRouter_HealthFailsWhenDependencyIsDown", + "tokens": 1153, + "hash": "c16425bf80bcc39b", + "size": 4034, + "mtimeMs": 1785668486703.307, + "updatedAt": "2026-10-06T11:06:20.890Z", + "source": "scan", + "symbols": [ + { + "name": "TestRouter_MtaSts_Autoconfig_SecurityTxt_Ingest", + "kind": "fn", + "startLine": 14, + "endLine": 70, + "tokens": 672 + }, + { + "name": "TestRouter_HealthReportsDegradedWithoutFailing", + "kind": "fn", + "startLine": 75, + "endLine": 92, + "tokens": 227 + }, + { + "name": "TestRouter_HealthFailsWhenDependencyIsDown", + "kind": "fn", + "startLine": 95, + "endLine": 105, + "tokens": 116 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go", + "services/protocols/internal/application/usecase/apply_rules_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/presentation/http/router.go": { + "description": "Router (45 fields); methods: SetMtaStsMode, ServeHTTP, SetDegradedCheck, SetMailAPI", + "tokens": 3672, + "hash": "ec0b09db271a4161", + "size": 12852, + "mtimeMs": 1788830725028.6208, + "updatedAt": "2026-10-06T11:06:20.891Z", + "source": "scan", + "symbols": [ + { + "name": "Router", + "kind": "class", + "startLine": 20, + "endLine": 49, + "tokens": 365 + }, + { + "name": "NewRouter", + "kind": "fn", + "startLine": 51, + "endLine": 65, + "tokens": 181 + }, + { + "name": "SetMtaStsMode", + "kind": "method", + "startLine": 70, + "endLine": 72, + "tokens": 24 + }, + { + "name": "ServeHTTP", + "kind": "method", + "startLine": 74, + "endLine": 76, + "tokens": 28 + }, + { + "name": "registerRoutes", + "kind": "method", + "startLine": 78, + "endLine": 104, + "tokens": 479 + }, + { + "name": "SetDegradedCheck", + "kind": "method", + "startLine": 109, + "endLine": 111, + "tokens": 24 + }, + { + "name": "handleHealth", + "kind": "method", + "startLine": 113, + "endLine": 137, + "tokens": 232 + }, + { + "name": "handleMtaSts", + "kind": "method", + "startLine": 139, + "endLine": 151, + "tokens": 164 + }, + { + "name": "handleSecurityTxt", + "kind": "method", + "startLine": 153, + "endLine": 164, + "tokens": 164 + }, + { + "name": "handleThunderbirdAutoconfig", + "kind": "method", + "startLine": 166, + "endLine": 175, + "tokens": 113 + }, + { + "name": "handleOutlookAutodiscover", + "kind": "method", + "startLine": 177, + "endLine": 186, + "tokens": 121 + }, + { + "name": "handleDmarcIngest", + "kind": "method", + "startLine": 188, + "endLine": 209, + "tokens": 205 + }, + { + "name": "handleTlsRptIngest", + "kind": "method", + "startLine": 211, + "endLine": 232, + "tokens": 206 + }, + { + "name": "extractHost", + "kind": "fn", + "startLine": 236, + "endLine": 244, + "tokens": 66 + }, + { + "name": "baseDomain", + "kind": "fn", + "startLine": 250, + "endLine": 257, + "tokens": 54 + }, + { + "name": "SetMailAPI", + "kind": "method", + "startLine": 262, + "endLine": 267, + "tokens": 66 + }, + { + "name": "mailReady", + "kind": "method", + "startLine": 269, + "endLine": 276, + "tokens": 69 + }, + { + "name": "handleMailFolders", + "kind": "method", + "startLine": 278, + "endLine": 282, + "tokens": 39 + }, + { + "name": "handleMailMessages", + "kind": "method", + "startLine": 284, + "endLine": 288, + "tokens": 40 + }, + { + "name": "handleMailMessage", + "kind": "method", + "startLine": 290, + "endLine": 294, + "tokens": 39 + }, + { + "name": "handleMailSeen", + "kind": "method", + "startLine": 296, + "endLine": 304, + "tokens": 75 + }, + { + "name": "handleMailSend", + "kind": "method", + "startLine": 306, + "endLine": 314, + "tokens": 75 + }, + { + "name": "handleMailDelete", + "kind": "method", + "startLine": 318, + "endLine": 326, + "tokens": 89 + }, + { + "name": "handleMailFolderAdmin", + "kind": "method", + "startLine": 329, + "endLine": 337, + "tokens": 79 + }, + { + "name": "SetFolderAdmin", + "kind": "method", + "startLine": 340, + "endLine": 344, + "tokens": 37 + }, + { + "name": "handleMailMove", + "kind": "method", + "startLine": 347, + "endLine": 355, + "tokens": 75 + }, + { + "name": "handleMailDraft", + "kind": "method", + "startLine": 357, + "endLine": 365, + "tokens": 77 + } + ], + "symbolSource": "ts", + "skeleton": "type Router struct {\nfunc NewRouter(reportUseCase *appusecase.IngestReportsUseCase, pingFunc func() error) *Router {\nfunc (r *Router) SetMtaStsMode(mode valueobject.MtaStsMode) {\nfunc (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {\nfunc (r *Router) registerRoutes() {\nfunc (r *Router) SetDegradedCheck(check func() error) {\nfunc (r *Router) handleHealth(w http.ResponseWriter, _ *http.Request) {\nfunc (r *Router) handleMtaSts(w http.ResponseWriter, req *http.Request) {\nfunc (r *Router) handleSecurityTxt(w http.ResponseWriter, req *http.Request) {\nfunc (r *Router) handleThunderbirdAutoconfig(w http.ResponseWriter, req *http.Request) {\nfunc (r *Router) handleOutlookAutodiscover(w http.ResponseWriter, req *http.Request) {\nfunc (r *Router) handleDmarcIngest(w http.ResponseWriter, req *http.Request) {\nfunc (r *Router) handleTlsRptIngest(w http.ResponseWriter, req *http.Request) {\nfunc extractHost(hostHeader string) string {\nfunc baseDomain(host string) string {\nfunc (r *Router) SetMailAPI(useCase *appusecase.WebmailUseCase, sessionSecret string) {\nfunc (r *Router) mailReady(w http.ResponseWriter) bool {\nfunc (r *Router) handleMailFolders(w http.ResponseWriter, req *http.Request) {\nfunc (r *Router) handleMailMessages(w http.ResponseWriter, req *http.Request) {\nfunc (r *Router) handleMailMessage(w http.ResponseWriter, req *http.Request) {", + "imports": [ + "services/protocols/internal/presentation/http/admin_dkim_api.go", + "services/protocols/internal/application/usecase/apply_rules_test.go", + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/presentation/http/websession_test.go": { + "description": "TestWebSessionVerifier_AcceptsTokenIssuedByTheAuthService, TestWebSessionVerifier_RejectsChallengeToken, TestWebSessionVerifier_RejectsWrongSecret,...", + "tokens": 1697, + "hash": "507ff34b5540498d", + "size": 5938, + "mtimeMs": 1787496118558.5413, + "updatedAt": "2026-10-06T11:06:20.891Z", + "source": "scan", + "symbols": [ + { + "name": "testVerifier", + "kind": "fn", + "startLine": 27, + "endLine": 31, + "tokens": 50 + }, + { + "name": "TestWebSessionVerifier_AcceptsTokenIssuedByTheAuthService", + "kind": "fn", + "startLine": 33, + "endLine": 44, + "tokens": 149 + }, + { + "name": "TestWebSessionVerifier_RejectsChallengeToken", + "kind": "fn", + "startLine": 48, + "endLine": 53, + "tokens": 64 + }, + { + "name": "TestWebSessionVerifier_RejectsWrongSecret", + "kind": "fn", + "startLine": 55, + "endLine": 60, + "tokens": 64 + }, + { + "name": "TestWebSessionVerifier_RejectsTamperedPayload", + "kind": "fn", + "startLine": 64, + "endLine": 69, + "tokens": 71 + }, + { + "name": "TestWebSessionVerifier_RequireSurfaceRejectsOtherSurface", + "kind": "fn", + "startLine": 71, + "endLine": 79, + "tokens": 117 + }, + { + "name": "TestWebSessionVerifier_RejectsMalformed", + "kind": "fn", + "startLine": 81, + "endLine": 88, + "tokens": 74 + }, + { + "name": "TestWebSessionVerifier_RejectsExpiredToken", + "kind": "fn", + "startLine": 91, + "endLine": 97, + "tokens": 90 + }, + { + "name": "mintSession", + "kind": "fn", + "startLine": 105, + "endLine": 108, + "tokens": 30 + }, + { + "name": "mintSessionRaw", + "kind": "fn", + "startLine": 112, + "endLine": 126, + "tokens": 136 + }, + { + "name": "TestMintedSessionVerifiesLikeARealOne", + "kind": "fn", + "startLine": 130, + "endLine": 144, + "tokens": 156 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/presentation/http/websession.go": { + "description": "WebSession (29 fields); methods: Verify, RequireSurface", + "tokens": 934, + "hash": "806a14f47694d53f", + "size": 3266, + "mtimeMs": 1785689161519.7087, + "updatedAt": "2026-10-06T11:06:20.891Z", + "source": "scan", + "symbols": [ + { + "name": "WebSession", + "kind": "class", + "startLine": 16, + "endLine": 27, + "tokens": 112 + }, + { + "name": "WebSessionVerifier", + "kind": "class", + "startLine": 43, + "endLine": 46, + "tokens": 22 + }, + { + "name": "NewWebSessionVerifier", + "kind": "fn", + "startLine": 48, + "endLine": 50, + "tokens": 38 + }, + { + "name": "Verify", + "kind": "method", + "startLine": 55, + "endLine": 89, + "tokens": 270 + }, + { + "name": "RequireSurface", + "kind": "method", + "startLine": 93, + "endLine": 102, + "tokens": 85 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/presentation/imap/backend_test.go": { + "description": "fakeAuthRepository (64 fields); methods: FindByAddress, ListFolders, FindByName, ListMessages", + "tokens": 6125, + "hash": "85ea4a755d6345db", + "size": 21437, + "mtimeMs": 1785635482529.1238, + "updatedAt": "2026-10-06T11:06:20.891Z", + "source": "scan", + "symbols": [ + { + "name": "fakeAuthRepository", + "kind": "class", + "startLine": 16, + "endLine": 18, + "tokens": 21 + }, + { + "name": "FindByAddress", + "kind": "method", + "startLine": 20, + "endLine": 26, + "tokens": 55 + }, + { + "name": "fakeImapFolderRepository", + "kind": "class", + "startLine": 28, + "endLine": 30, + "tokens": 24 + }, + { + "name": "ListFolders", + "kind": "method", + "startLine": 32, + "endLine": 38, + "tokens": 64 + }, + { + "name": "FindByName", + "kind": "method", + "startLine": 40, + "endLine": 46, + "tokens": 57 + }, + { + "name": "fakeMessageQueryRepository", + "kind": "class", + "startLine": 48, + "endLine": 50, + "tokens": 21 + }, + { + "name": "ListMessages", + "kind": "method", + "startLine": 52, + "endLine": 54, + "tokens": 39 + }, + { + "name": "fakeFlagRepository", + "kind": "class", + "startLine": 56, + "endLine": 63, + "tokens": 43 + }, + { + "name": "fakeFlagCall", + "kind": "class", + "startLine": 65, + "endLine": 70, + "tokens": 30 + }, + { + "name": "SetFlags", + "kind": "method", + "startLine": 72, + "endLine": 76, + "tokens": 96 + }, + { + "name": "fakeBlobReader", + "kind": "class", + "startLine": 78, + "endLine": 80, + "tokens": 18 + }, + { + "name": "ReadByID", + "kind": "method", + "startLine": 82, + "endLine": 84, + "tokens": 35 + }, + { + "name": "fakeExpungeRepository", + "kind": "class", + "startLine": 89, + "endLine": 94, + "tokens": 28 + }, + { + "name": "Expunge", + "kind": "method", + "startLine": 96, + "endLine": 102, + "tokens": 59 + }, + { + "name": "fakeCopyRepository", + "kind": "class", + "startLine": 104, + "endLine": 106, + "tokens": 18 + }, + { + "name": "CopyMessages", + "kind": "method", + "startLine": 108, + "endLine": 110, + "tokens": 43 + }, + { + "name": "newTestSession", + "kind": "fn", + "startLine": 122, + "endLine": 129, + "tokens": 169 + }, + { + "name": "newTestSessionWithExpungeAndCopy", + "kind": "fn", + "startLine": 131, + "endLine": 146, + "tokens": 177 + }, + { + "name": "TestSession_Login_SucceedsForCorrectCredentials", + "kind": "fn", + "startLine": 148, + "endLine": 158, + "tokens": 113 + }, + { + "name": "TestSession_Login_FailsForWrongPassword", + "kind": "fn", + "startLine": 160, + "endLine": 177, + "tokens": 176 + }, + { + "name": "TestSession_Select_ReturnsDataForKnownFolder", + "kind": "fn", + "startLine": 179, + "endLine": 199, + "tokens": 255 + }, + { + "name": "TestSession_Select_FailsForUnknownFolder", + "kind": "fn", + "startLine": 201, + "endLine": 214, + "tokens": 178 + }, + { + "name": "TestSession_Store_CallsFlagRepositoryWithCorrectArgs", + "kind": "fn", + "startLine": 216, + "endLine": 244, + "tokens": 340 + }, + { + "name": "TestSession_Store_HandlesUIDStarRange", + "kind": "fn", + "startLine": 250, + "endLine": 297, + "tokens": 450 + }, + { + "name": "TestSession_Store_AllowsSequenceNumberSet", + "kind": "fn", + "startLine": 304, + "endLine": 341, + "tokens": 391 + }, + { + "name": "TestMatchFolders_FiltersByWildcardPattern", + "kind": "fn", + "startLine": 351, + "endLine": 418, + "tokens": 475 + }, + { + "name": "TestSession_List_UsesFolderRepository", + "kind": "fn", + "startLine": 426, + "endLine": 447, + "tokens": 254 + }, + { + "name": "TestSession_Expunge_CallsUseCaseWithDeletedUIDsOnly", + "kind": "fn", + "startLine": 449, + "endLine": 477, + "tokens": 357 + }, + { + "name": "TestSession_Copy_CallsUseCaseAndBuildsCopyData", + "kind": "fn", + "startLine": 479, + "endLine": 506, + "tokens": 357 + }, + { + "name": "TestSession_Copy_FailsForUnknownDestination", + "kind": "fn", + "startLine": 508, + "endLine": 522, + "tokens": 221 + } + ], + "symbolSource": "ts", + "skeleton": "type fakeAuthRepository struct {\nfunc (f *fakeAuthRepository) FindByAddress(_ context.Context, address string) (*port.MailboxAuth, error) {\ntype fakeImapFolderRepository struct {\nfunc (f *fakeImapFolderRepository) ListFolders(_ context.Context, _ string) ([]port.ImapFolderRecord, error) {\nfunc (f *fakeImapFolderRepository) FindByName(_ context.Context, _ string, name string) (*port.ImapFolderRecord, error) {\ntype fakeMessageQueryRepository struct {\nfunc (f *fakeMessageQueryRepository) ListMessages(_ context.Context, _ string) ([]port.MessageRecord, error) {\ntype fakeFlagRepository struct {\ntype fakeFlagCall struct {\nfunc (f *fakeFlagRepository) SetFlags(_ context.Context, folderID string, uid uint32, op port.FlagOp, flags []string, _ uint64) (bool, error) {\ntype fakeBlobReader struct {\nfunc (f *fakeBlobReader) ReadByID(_ context.Context, blobID uuid.UUID) ([]byte, error) {\ntype fakeExpungeRepository struct {\nfunc (f *fakeExpungeRepository) Expunge(_ context.Context, folderID string, uids []uint32) error {\ntype fakeCopyRepository struct {\nfunc (f *fakeCopyRepository) CopyMessages(_ context.Context, _ string, _ []uint32, _ string) ([]port.CopiedMessage, error) {\nfunc newTestSession(auth *fakeAuthRepository, folders *fakeImapFolderRepository, messages *fakeMessageQueryRepository, flags *fakeFlagRepository, blobs *fakeBlobReader) imapserver.Session {", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/application/usecase/apply_rules_test.go" + ], + "importance": 0.0216 + }, + "services/protocols/internal/presentation/imap/backend.go": { + "description": "session (112 fields); methods: Close, Login, Select, Unselect", + "tokens": 4188, + "hash": "5d15e8f0d1163805", + "size": 14658, + "mtimeMs": 1785635482528.997, + "updatedAt": "2026-10-06T11:06:20.891Z", + "source": "scan", + "symbols": [ + { + "name": "session", + "kind": "class", + "startLine": 19, + "endLine": 24, + "tokens": 50 + }, + { + "name": "NewSession", + "kind": "fn", + "startLine": 27, + "endLine": 29, + "tokens": 54 + }, + { + "name": "toIMAPError", + "kind": "fn", + "startLine": 31, + "endLine": 40, + "tokens": 98 + }, + { + "name": "Close", + "kind": "method", + "startLine": 42, + "endLine": 48, + "tokens": 32 + }, + { + "name": "Login", + "kind": "method", + "startLine": 50, + "endLine": 57, + "tokens": 63 + }, + { + "name": "Select", + "kind": "method", + "startLine": 59, + "endLine": 78, + "tokens": 175 + }, + { + "name": "Unselect", + "kind": "method", + "startLine": 80, + "endLine": 87, + "tokens": 40 + }, + { + "name": "Status", + "kind": "method", + "startLine": 89, + "endLine": 109, + "tokens": 163 + }, + { + "name": "Poll", + "kind": "method", + "startLine": 111, + "endLine": 116, + "tokens": 46 + }, + { + "name": "Idle", + "kind": "method", + "startLine": 118, + "endLine": 123, + "tokens": 44 + }, + { + "name": "List", + "kind": "method", + "startLine": 125, + "endLine": 136, + "tokens": 117 + }, + { + "name": "matchFolders", + "kind": "fn", + "startLine": 145, + "endLine": 156, + "tokens": 93 + }, + { + "name": "numSetIter", + "kind": "method", + "startLine": 163, + "endLine": 190, + "tokens": 203 + }, + { + "name": "Fetch", + "kind": "method", + "startLine": 192, + "endLine": 213, + "tokens": 199 + }, + { + "name": "writeFetchResponse", + "kind": "method", + "startLine": 215, + "endLine": 261, + "tokens": 387 + }, + { + "name": "expungeUIDs", + "kind": "method", + "startLine": 278, + "endLine": 322, + "tokens": 337 + }, + { + "name": "Expunge", + "kind": "method", + "startLine": 324, + "endLine": 348, + "tokens": 227 + }, + { + "name": "Copy", + "kind": "method", + "startLine": 350, + "endLine": 382, + "tokens": 359 + }, + { + "name": "Move", + "kind": "method", + "startLine": 392, + "endLine": 425, + "tokens": 258 + }, + { + "name": "Store", + "kind": "method", + "startLine": 427, + "endLine": 478, + "tokens": 403 + } + ], + "symbolSource": "ts", + "skeleton": "type session struct {\nfunc NewSession(_ *imapserver.Conn, uc *usecase.ImapSessionUseCase) (imapserver.Session, *imapserver.GreetingData, error) {\nfunc toIMAPError(err error) error {\nfunc (s *session) Close() error {\nfunc (s *session) Login(username, password string) error {\nfunc (s *session) Select(mailbox string, _ *imap.SelectOptions) (*imap.SelectData, error) {\nfunc (s *session) Unselect() error {\nfunc (s *session) Status(mailbox string, options *imap.StatusOptions) (*imap.StatusData, error) {\nfunc (s *session) Poll(w *imapserver.UpdateWriter, allowExpunge bool) error {\nfunc (s *session) Idle(w *imapserver.UpdateWriter, stop <-chan struct{}) error {\nfunc (s *session) List(w *imapserver.ListWriter, ref string, patterns []string, options *imap.ListOptions) error {\nfunc matchFolders(recs []port.ImapFolderRecord, ref string, patterns []string) []port.ImapFolderRecord {\nfunc (s *session) numSetIter(numSet imap.NumSet, messages []port.MessageRecord, f func(seqNum uint32, msg port.MessageRecord)) {\nfunc (s *session) Fetch(w *imapserver.FetchWriter, numSet imap.NumSet, options *imap.FetchOptions) error {\nfunc (s *session) writeFetchResponse(w *imapserver.FetchResponseWriter, msg port.MessageRecord, options *imap.FetchOptions) error {\nfunc (s *session) expungeUIDs(uids *imap.UIDSet, requireDeleted bool) ([]uint32, error) {", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/application/usecase/apply_rules_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/presentation/imap/search_test.go": { + "description": "TestSession_Search_MatchesByFlag, TestSession_Search_MatchesByUIDSetIntersection, TestSession_Search_MatchesByHeaderField, TestSession_Search_NotCr...", + "tokens": 1457, + "hash": "75e14ffc39e16db9", + "size": 5099, + "mtimeMs": 1785635482529.1238, + "updatedAt": "2026-10-06T11:06:20.891Z", + "source": "scan", + "symbols": [ + { + "name": "TestSession_Search_MatchesByFlag", + "kind": "fn", + "startLine": 14, + "endLine": 45, + "tokens": 368 + }, + { + "name": "TestSession_Search_MatchesByUIDSetIntersection", + "kind": "fn", + "startLine": 47, + "endLine": 73, + "tokens": 323 + }, + { + "name": "TestSession_Search_MatchesByHeaderField", + "kind": "fn", + "startLine": 75, + "endLine": 106, + "tokens": 381 + }, + { + "name": "TestSession_Search_NotCriteriaExcludesMatches", + "kind": "fn", + "startLine": 108, + "endLine": 133, + "tokens": 323 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/presentation/imap/search.go": { + "description": "Package imappresentation: this file implements IMAP SEARCH (RFC 3501", + "tokens": 2395, + "hash": "fbcdf737184dc313", + "size": 8382, + "mtimeMs": 1785635482529.1238, + "updatedAt": "2026-10-06T11:06:20.891Z", + "source": "scan", + "symbols": [ + { + "name": "Search", + "kind": "method", + "startLine": 24, + "endLine": 76, + "tokens": 368 + }, + { + "name": "matchCriteria", + "kind": "fn", + "startLine": 84, + "endLine": 180, + "tokens": 704 + }, + { + "name": "canonicalFlag", + "kind": "fn", + "startLine": 184, + "endLine": 186, + "tokens": 26 + }, + { + "name": "matchDate", + "kind": "fn", + "startLine": 188, + "endLine": 198, + "tokens": 94 + }, + { + "name": "matchHeaderField", + "kind": "fn", + "startLine": 200, + "endLine": 212, + "tokens": 92 + }, + { + "name": "parseDateHeader", + "kind": "fn", + "startLine": 228, + "endLine": 231, + "tokens": 42 + }, + { + "name": "matchEntity", + "kind": "fn", + "startLine": 239, + "endLine": 248, + "tokens": 84 + }, + { + "name": "matchEntityRecursive", + "kind": "fn", + "startLine": 250, + "endLine": 279, + "tokens": 179 + }, + { + "name": "matchHeaderFieldsLower", + "kind": "fn", + "startLine": 281, + "endLine": 289, + "tokens": 65 + } + ], + "symbolSource": "ts", + "skeleton": "func (s *session) Search(numKind imapserver.NumKind, criteria *imap.SearchCriteria, _ *imap.SearchOptions) (*imap.SearchData, error) {\nfunc matchCriteria(seqNum, uid uint32, receivedAt time.Time, sizeBytes int64, modSeq uint64, flags []imap.Flag, criteria *imap.SearchCriteria, loadRaw func() ([]byte, error)) bool {\nfunc canonicalFlag(f imap.Flag) imap.Flag {\nfunc matchDate(t, since, before time.Time) bool {\nfunc matchHeaderField(header textproto.Header, key, pattern string) bool {\nfunc parseDateHeader(header textproto.Header) (time.Time, error) {\nfunc matchEntity(raw []byte, pattern string, includeHeader bool) bool {\nfunc matchEntityRecursive(e *gomessage.Entity, patternLower string, includeHeader bool) bool {\nfunc matchHeaderFieldsLower(fields gomessage.HeaderFields, patternLower string) bool {", + "imports": [ + "services/webmail/src/app/(user)/user/mail/[folder]/page.tsx" + ], + "importance": 0.0144 + }, + "services/protocols/internal/presentation/imap/stubs.go": { + "description": "adapts github.com/emersion/go-imap/v2's", + "tokens": 373, + "hash": "deebfa03b60ea5e4", + "size": 1305, + "mtimeMs": 1785635482529.1238, + "updatedAt": "2026-10-06T11:06:20.891Z", + "source": "scan", + "importance": 0.0144 + }, + "services/protocols/internal/presentation/managesieve/conn_test.go": { + "description": "fakeAuthRepository (42 fields); methods: FindByAddress, GetScript, PutScript, ListScripts", + "tokens": 1681, + "hash": "a027562f989db32f", + "size": 5882, + "mtimeMs": 1785635482529.4133, + "updatedAt": "2026-10-06T11:06:20.892Z", + "source": "scan", + "symbols": [ + { + "name": "fakeAuthRepository", + "kind": "class", + "startLine": 19, + "endLine": 21, + "tokens": 21 + }, + { + "name": "FindByAddress", + "kind": "method", + "startLine": 23, + "endLine": 29, + "tokens": 55 + }, + { + "name": "fakeSieveRepository", + "kind": "class", + "startLine": 31, + "endLine": 33, + "tokens": 26 + }, + { + "name": "newFakeSieveRepository", + "kind": "fn", + "startLine": 35, + "endLine": 37, + "tokens": 42 + }, + { + "name": "GetScript", + "kind": "method", + "startLine": 39, + "endLine": 49, + "tokens": 72 + }, + { + "name": "PutScript", + "kind": "method", + "startLine": 51, + "endLine": 70, + "tokens": 140 + }, + { + "name": "ListScripts", + "kind": "method", + "startLine": 72, + "endLine": 82, + "tokens": 82 + }, + { + "name": "SetActiveScript", + "kind": "method", + "startLine": 84, + "endLine": 94, + "tokens": 69 + }, + { + "name": "DeleteScript", + "kind": "method", + "startLine": 96, + "endLine": 103, + "tokens": 53 + }, + { + "name": "RenameScript", + "kind": "method", + "startLine": 105, + "endLine": 115, + "tokens": 74 + }, + { + "name": "setupManageSieveServer", + "kind": "fn", + "startLine": 117, + "endLine": 143, + "tokens": 200 + }, + { + "name": "TestManageSieveServer_FullLifecycle", + "kind": "fn", + "startLine": 145, + "endLine": 243, + "tokens": 762 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/application/usecase/apply_rules_test.go" + ], + "importance": 0.0208 + }, + "services/protocols/internal/presentation/managesieve/conn.go": { + "description": "conn (74 fields)", + "tokens": 2789, + "hash": "6040be249dc47098", + "size": 9761, + "mtimeMs": 1785635482529.1238, + "updatedAt": "2026-10-06T11:06:20.892Z", + "source": "scan", + "symbols": [ + { + "name": "state", + "kind": "class", + "startLine": 19, + "endLine": 19, + "tokens": 4 + }, + { + "name": "conn", + "kind": "class", + "startLine": 26, + "endLine": 38, + "tokens": 70 + }, + { + "name": "newConn", + "kind": "fn", + "startLine": 40, + "endLine": 49, + "tokens": 85 + }, + { + "name": "serve", + "kind": "method", + "startLine": 51, + "endLine": 107, + "tokens": 339 + }, + { + "name": "writeLine", + "kind": "method", + "startLine": 109, + "endLine": 114, + "tokens": 43 + }, + { + "name": "sendCapabilities", + "kind": "method", + "startLine": 116, + "endLine": 124, + "tokens": 106 + }, + { + "name": "handleCapability", + "kind": "method", + "startLine": 126, + "endLine": 129, + "tokens": 30 + }, + { + "name": "handleStartTLS", + "kind": "method", + "startLine": 131, + "endLine": 156, + "tokens": 182 + }, + { + "name": "handleAuthenticate", + "kind": "method", + "startLine": 158, + "endLine": 214, + "tokens": 384 + }, + { + "name": "handlePutScript", + "kind": "method", + "startLine": 216, + "endLine": 235, + "tokens": 132 + }, + { + "name": "handleGetScript", + "kind": "method", + "startLine": 237, + "endLine": 258, + "tokens": 155 + }, + { + "name": "handleSetActive", + "kind": "method", + "startLine": 260, + "endLine": 274, + "tokens": 104 + }, + { + "name": "handleHaveSpace", + "kind": "method", + "startLine": 276, + "endLine": 288, + "tokens": 105 + }, + { + "name": "handleListScripts", + "kind": "method", + "startLine": 290, + "endLine": 311, + "tokens": 147 + }, + { + "name": "handleDeleteScript", + "kind": "method", + "startLine": 313, + "endLine": 332, + "tokens": 128 + }, + { + "name": "handleRenameScript", + "kind": "method", + "startLine": 334, + "endLine": 353, + "tokens": 138 + }, + { + "name": "handleCheckScript", + "kind": "method", + "startLine": 355, + "endLine": 377, + "tokens": 140 + }, + { + "name": "parseStringLiteral", + "kind": "method", + "startLine": 379, + "endLine": 385, + "tokens": 66 + }, + { + "name": "parseTwoStrings", + "kind": "method", + "startLine": 387, + "endLine": 394, + "tokens": 56 + }, + { + "name": "parseNameAndContent", + "kind": "method", + "startLine": 396, + "endLine": 428, + "tokens": 282 + } + ], + "symbolSource": "ts", + "skeleton": "type state int\ntype conn struct {\nfunc newConn(c net.Conn, useCase *appusecase.ManageSieveSessionUseCase, tlsProvider port.CertProvider) *conn {\nfunc (c *conn) serve() {\nfunc (c *conn) writeLine(msg string) error {\nfunc (c *conn) sendCapabilities() {\nfunc (c *conn) handleCapability() {\nfunc (c *conn) handleStartTLS() bool {\nfunc (c *conn) handleAuthenticate(arg string) {\nfunc (c *conn) handlePutScript(arg string) {\nfunc (c *conn) handleGetScript(arg string) {\nfunc (c *conn) handleSetActive(arg string) {\nfunc (c *conn) handleHaveSpace(arg string) {\nfunc (c *conn) handleListScripts() {\nfunc (c *conn) handleDeleteScript(arg string) {\nfunc (c *conn) handleRenameScript(arg string) {\nfunc (c *conn) handleCheckScript(arg string) {\nfunc (c *conn) parseStringLiteral(input string) string {\nfunc (c *conn) parseTwoStrings(input string) []string {\nfunc (c *conn) parseNameAndContent(arg string) (string, string, error) {", + "imports": [ + "services/protocols/internal/infrastructure/tls/acme_cert_watcher_test.go", + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/application/usecase/apply_rules_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/presentation/managesieve/server.go": { + "description": "Server (14 fields); methods: ListenAndServe, Serve, Close", + "tokens": 299, + "hash": "f9e1f034b0cc372b", + "size": 1045, + "mtimeMs": 1785635482529.4133, + "updatedAt": "2026-10-06T11:06:20.892Z", + "source": "scan", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/application/usecase/apply_rules_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/presentation/pop3/conn_test.go": { + "description": "fakeAuthRepository (52 fields); methods: FindByAddress, ListFolders, FindByName, ListMessages", + "tokens": 2304, + "hash": "a3e6c0e1fda2a0b7", + "size": 8063, + "mtimeMs": 1785635482529.7332, + "updatedAt": "2026-10-06T11:06:20.892Z", + "source": "scan", + "symbols": [ + { + "name": "fakeAuthRepository", + "kind": "class", + "startLine": 18, + "endLine": 20, + "tokens": 21 + }, + { + "name": "FindByAddress", + "kind": "method", + "startLine": 22, + "endLine": 28, + "tokens": 55 + }, + { + "name": "fakeImapFolderRepository", + "kind": "class", + "startLine": 30, + "endLine": 32, + "tokens": 24 + }, + { + "name": "ListFolders", + "kind": "method", + "startLine": 34, + "endLine": 40, + "tokens": 64 + }, + { + "name": "FindByName", + "kind": "method", + "startLine": 42, + "endLine": 48, + "tokens": 57 + }, + { + "name": "fakeMessageQueryRepository", + "kind": "class", + "startLine": 50, + "endLine": 52, + "tokens": 21 + }, + { + "name": "ListMessages", + "kind": "method", + "startLine": 54, + "endLine": 56, + "tokens": 39 + }, + { + "name": "fakeBlobReader", + "kind": "class", + "startLine": 58, + "endLine": 60, + "tokens": 18 + }, + { + "name": "ReadByID", + "kind": "method", + "startLine": 62, + "endLine": 64, + "tokens": 35 + }, + { + "name": "fakeExpungeRepository", + "kind": "class", + "startLine": 66, + "endLine": 71, + "tokens": 28 + }, + { + "name": "Expunge", + "kind": "method", + "startLine": 73, + "endLine": 79, + "tokens": 59 + }, + { + "name": "setupPop3Server", + "kind": "fn", + "startLine": 81, + "endLine": 111, + "tokens": 293 + }, + { + "name": "TestPop3Server_Capa_User_Pass_Stat_List_Retr_Dele_Quit", + "kind": "fn", + "startLine": 113, + "endLine": 252, + "tokens": 944 + }, + { + "name": "TestPop3Server_Top_Rset_AuthFailures", + "kind": "fn", + "startLine": 254, + "endLine": 337, + "tokens": 566 + } + ], + "symbolSource": "ts", + "skeleton": "type fakeAuthRepository struct {\nfunc (f *fakeAuthRepository) FindByAddress(_ context.Context, address string) (*port.MailboxAuth, error) {\ntype fakeImapFolderRepository struct {\nfunc (f *fakeImapFolderRepository) ListFolders(_ context.Context, _ string) ([]port.ImapFolderRecord, error) {\nfunc (f *fakeImapFolderRepository) FindByName(_ context.Context, _ string, name string) (*port.ImapFolderRecord, error) {\ntype fakeMessageQueryRepository struct {\nfunc (f *fakeMessageQueryRepository) ListMessages(_ context.Context, _ string) ([]port.MessageRecord, error) {\ntype fakeBlobReader struct {\nfunc (f *fakeBlobReader) ReadByID(_ context.Context, blobID uuid.UUID) ([]byte, error) {\ntype fakeExpungeRepository struct {\nfunc (f *fakeExpungeRepository) Expunge(_ context.Context, folderID string, uids []uint32) error {\nfunc setupPop3Server(t *testing.T, messages []port.MessageRecord, blobs map[uuid.UUID][]byte, expunger *fakeExpungeRepository) (net.Listener, func()) {\nfunc TestPop3Server_Capa_User_Pass_Stat_List_Retr_Dele_Quit(t *testing.T) {\nfunc TestPop3Server_Top_Rset_AuthFailures(t *testing.T) {", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/application/usecase/apply_rules_test.go" + ], + "importance": 0.0192 + }, + "services/protocols/internal/presentation/pop3/conn.go": { + "description": "pop3Msg (82 fields)", + "tokens": 2806, + "hash": "25c93145f8881a37", + "size": 9819, + "mtimeMs": 1785635482529.4133, + "updatedAt": "2026-10-06T11:06:20.892Z", + "source": "scan", + "symbols": [ + { + "name": "state", + "kind": "class", + "startLine": 17, + "endLine": 17, + "tokens": 4 + }, + { + "name": "pop3Msg", + "kind": "class", + "startLine": 25, + "endLine": 29, + "tokens": 23 + }, + { + "name": "conn", + "kind": "class", + "startLine": 31, + "endLine": 45, + "tokens": 80 + }, + { + "name": "newConn", + "kind": "fn", + "startLine": 47, + "endLine": 56, + "tokens": 83 + }, + { + "name": "serve", + "kind": "method", + "startLine": 58, + "endLine": 117, + "tokens": 298 + }, + { + "name": "writeLine", + "kind": "method", + "startLine": 119, + "endLine": 124, + "tokens": 43 + }, + { + "name": "handleCapa", + "kind": "method", + "startLine": 126, + "endLine": 139, + "tokens": 130 + }, + { + "name": "handleStls", + "kind": "method", + "startLine": 141, + "endLine": 166, + "tokens": 179 + }, + { + "name": "handleUser", + "kind": "method", + "startLine": 168, + "endLine": 179, + "tokens": 72 + }, + { + "name": "handlePass", + "kind": "method", + "startLine": 181, + "endLine": 214, + "tokens": 240 + }, + { + "name": "handleStat", + "kind": "method", + "startLine": 216, + "endLine": 223, + "tokens": 62 + }, + { + "name": "statActive", + "kind": "method", + "startLine": 225, + "endLine": 235, + "tokens": 62 + }, + { + "name": "handleList", + "kind": "method", + "startLine": 237, + "endLine": 261, + "tokens": 209 + }, + { + "name": "handleUidl", + "kind": "method", + "startLine": 263, + "endLine": 286, + "tokens": 186 + }, + { + "name": "handleRetr", + "kind": "method", + "startLine": 288, + "endLine": 311, + "tokens": 186 + }, + { + "name": "handleTop", + "kind": "method", + "startLine": 313, + "endLine": 343, + "tokens": 257 + }, + { + "name": "extractTopLines", + "kind": "fn", + "startLine": 345, + "endLine": 372, + "tokens": 186 + }, + { + "name": "writeDotStuffedContent", + "kind": "method", + "startLine": 374, + "endLine": 384, + "tokens": 80 + }, + { + "name": "handleDele", + "kind": "method", + "startLine": 386, + "endLine": 399, + "tokens": 122 + }, + { + "name": "handleRset", + "kind": "method", + "startLine": 401, + "endLine": 410, + "tokens": 63 + }, + { + "name": "handleNoop", + "kind": "method", + "startLine": 412, + "endLine": 414, + "tokens": 15 + }, + { + "name": "handleQuit", + "kind": "method", + "startLine": 416, + "endLine": 436, + "tokens": 137 + } + ], + "symbolSource": "ts", + "skeleton": "type state int\ntype pop3Msg struct {\ntype conn struct {\nfunc newConn(c net.Conn, useCase *appusecase.Pop3SessionUseCase, tlsProvider port.CertProvider) *conn {\nfunc (c *conn) serve() {\nfunc (c *conn) writeLine(msg string) error {\nfunc (c *conn) handleCapa() {\nfunc (c *conn) handleStls() bool {\nfunc (c *conn) handleUser(arg string) {\nfunc (c *conn) handlePass(password string) {\nfunc (c *conn) handleStat() {\nfunc (c *conn) statActive() (int, int64) {\nfunc (c *conn) handleList(arg string) {\nfunc (c *conn) handleUidl(arg string) {\nfunc (c *conn) handleRetr(arg string) {\nfunc (c *conn) handleTop(arg string) {\nfunc extractTopLines(content []byte, n int) []byte {\nfunc (c *conn) writeDotStuffedContent(content []byte) {\nfunc (c *conn) handleDele(arg string) {\nfunc (c *conn) handleRset() {\nfunc (c *conn) handleNoop() {\nfunc (c *conn) handleQuit() {", + "imports": [ + "services/protocols/internal/infrastructure/tls/acme_cert_watcher_test.go", + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/application/usecase/apply_rules_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/presentation/pop3/server.go": { + "description": "Server (14 fields); methods: ListenAndServe, Serve, Close", + "tokens": 293, + "hash": "a5c9695f3f05fa15", + "size": 1024, + "mtimeMs": 1785635482529.7332, + "updatedAt": "2026-10-06T11:06:20.892Z", + "source": "scan", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/application/usecase/apply_rules_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/presentation/smtp/backend_test.go": { + "description": "fakeMailboxRepository (58 fields); methods: FindActiveByAddress, ResolveDeliveryTargets, Store, Scan", + "tokens": 3106, + "hash": "5b16b5e37ab93a1b", + "size": 10871, + "mtimeMs": 1785668486703.4868, + "updatedAt": "2026-10-06T11:06:20.893Z", + "source": "scan", + "symbols": [ + { + "name": "fakeMailboxRepository", + "kind": "class", + "startLine": 18, + "endLine": 20, + "tokens": 23 + }, + { + "name": "FindActiveByAddress", + "kind": "method", + "startLine": 22, + "endLine": 28, + "tokens": 58 + }, + { + "name": "ResolveDeliveryTargets", + "kind": "method", + "startLine": 30, + "endLine": 36, + "tokens": 70 + }, + { + "name": "recordingBlobStorage", + "kind": "class", + "startLine": 38, + "endLine": 43, + "tokens": 45 + }, + { + "name": "Store", + "kind": "method", + "startLine": 45, + "endLine": 55, + "tokens": 75 + }, + { + "name": "rejectingScanner", + "kind": "class", + "startLine": 58, + "endLine": 58, + "tokens": 14 + }, + { + "name": "Scan", + "kind": "method", + "startLine": 60, + "endLine": 62, + "tokens": 38 + }, + { + "name": "recordingMessageRepository", + "kind": "class", + "startLine": 64, + "endLine": 66, + "tokens": 25 + }, + { + "name": "PersistAll", + "kind": "method", + "startLine": 68, + "endLine": 75, + "tokens": 89 + }, + { + "name": "newTestSession", + "kind": "fn", + "startLine": 77, + "endLine": 79, + "tokens": 59 + }, + { + "name": "newTestSessionWithScanner", + "kind": "fn", + "startLine": 83, + "endLine": 94, + "tokens": 126 + }, + { + "name": "TestSession_Rcpt_AcceptsKnownActiveRecipient", + "kind": "fn", + "startLine": 96, + "endLine": 108, + "tokens": 159 + }, + { + "name": "TestSession_Rcpt_Rejects550ForUnknownRecipient", + "kind": "fn", + "startLine": 110, + "endLine": 126, + "tokens": 189 + }, + { + "name": "TestSession_Data_PersistsOnceForSingleRecipient", + "kind": "fn", + "startLine": 128, + "endLine": 154, + "tokens": 305 + }, + { + "name": "TestSession_Rcpt_Rejects452ForFullMailbox", + "kind": "fn", + "startLine": 156, + "endLine": 174, + "tokens": 208 + }, + { + "name": "TestSession_Rcpt_DeduplicatesRepeatedAddress", + "kind": "fn", + "startLine": 176, + "endLine": 200, + "tokens": 294 + }, + { + "name": "TestSession_Reset_ClearsAccumulatedRecipients", + "kind": "fn", + "startLine": 202, + "endLine": 222, + "tokens": 256 + }, + { + "name": "TestSession_Data_RendersUseCaseRejectionVerbatim", + "kind": "fn", + "startLine": 227, + "endLine": 272, + "tokens": 563 + }, + { + "name": "TestSession_Data_ReportsInternalFailureAsTemporary", + "kind": "fn", + "startLine": 276, + "endLine": 300, + "tokens": 250 + } + ], + "symbolSource": "ts", + "skeleton": "type fakeMailboxRepository struct {\nfunc (f *fakeMailboxRepository) FindActiveByAddress(_ context.Context, address string) (*port.MailboxRecord, error) {\nfunc (f *fakeMailboxRepository) ResolveDeliveryTargets(_ context.Context, address string) ([]port.MailboxRecord, error) {\ntype recordingBlobStorage struct {\nfunc (b *recordingBlobStorage) Store(_ context.Context, r io.Reader) (port.BlobRef, error) {\ntype rejectingScanner struct{ rejection error }\nfunc (s *rejectingScanner) Scan(_ context.Context, _ port.ScanInput) (*valueobject.ScanResult, error) {\ntype recordingMessageRepository struct {\nfunc (m *recordingMessageRepository) PersistAll(_ context.Context, inputs []port.PersistInboundMessageInput) ([]int64, error) {\nfunc newTestSession(mailboxes *fakeMailboxRepository, blobs *recordingBlobStorage, messages *recordingMessageRepository) gosmtp.Session {\nfunc newTestSessionWithScanner(mailboxes *fakeMailboxRepository, blobs *recordingBlobStorage, messages *recordingMessageRepository, rejection error) gosmtp.Session {\nfunc TestSession_Rcpt_AcceptsKnownActiveRecipient(t *testing.T) {\nfunc TestSession_Rcpt_Rejects550ForUnknownRecipient(t *testing.T) {\nfunc TestSession_Data_PersistsOnceForSingleRecipient(t *testing.T) {\nfunc TestSession_Rcpt_Rejects452ForFullMailbox(t *testing.T) {\nfunc TestSession_Rcpt_DeduplicatesRepeatedAddress(t *testing.T) {", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/application/usecase/apply_rules_test.go", + "services/protocols/internal/domain/valueobject/autoconfig_xml_test.go" + ], + "importance": 0.0315 + }, + "services/protocols/internal/presentation/smtp/backend.go": { + "description": "adapts github.com/emersion/go-smtp's Backend/", + "tokens": 1399, + "hash": "fbdcd64157d62c7a", + "size": 4895, + "mtimeMs": 1785689161519.86, + "updatedAt": "2026-10-06T11:06:20.893Z", + "source": "scan", + "symbols": [ + { + "name": "Backend", + "kind": "class", + "startLine": 20, + "endLine": 22, + "tokens": 20 + }, + { + "name": "NewBackend", + "kind": "fn", + "startLine": 24, + "endLine": 26, + "tokens": 28 + }, + { + "name": "NewSession", + "kind": "method", + "startLine": 28, + "endLine": 30, + "tokens": 36 + }, + { + "name": "session", + "kind": "class", + "startLine": 34, + "endLine": 40, + "tokens": 60 + }, + { + "name": "clientIP", + "kind": "method", + "startLine": 45, + "endLine": 57, + "tokens": 90 + }, + { + "name": "heloDomain", + "kind": "method", + "startLine": 59, + "endLine": 64, + "tokens": 30 + }, + { + "name": "Reset", + "kind": "method", + "startLine": 66, + "endLine": 70, + "tokens": 26 + }, + { + "name": "Logout", + "kind": "method", + "startLine": 72, + "endLine": 74, + "tokens": 14 + }, + { + "name": "Mail", + "kind": "method", + "startLine": 76, + "endLine": 79, + "tokens": 28 + }, + { + "name": "Rcpt", + "kind": "method", + "startLine": 81, + "endLine": 121, + "tokens": 366 + }, + { + "name": "Data", + "kind": "method", + "startLine": 123, + "endLine": 172, + "tokens": 473 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/application/usecase/apply_rules_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/presentation/smtp/sasl_login_test.go": { + "description": "TestLoginServer_PromptsThenAuthenticates, TestLoginServer_AcceptsInitialResponse, TestLoginServer_PropagatesAuthFailure, TestLoginServer_RefusesExt...", + "tokens": 614, + "hash": "6713e1f5d7a814b4", + "size": 2147, + "mtimeMs": 1785635482529.9143, + "updatedAt": "2026-10-06T11:06:20.893Z", + "source": "scan", + "symbols": [ + { + "name": "TestLoginServer_PromptsThenAuthenticates", + "kind": "fn", + "startLine": 10, + "endLine": 34, + "tokens": 243 + }, + { + "name": "TestLoginServer_AcceptsInitialResponse", + "kind": "fn", + "startLine": 38, + "endLine": 45, + "tokens": 93 + }, + { + "name": "TestLoginServer_PropagatesAuthFailure", + "kind": "fn", + "startLine": 47, + "endLine": 56, + "tokens": 110 + }, + { + "name": "TestLoginServer_RefusesExtraSteps", + "kind": "fn", + "startLine": 58, + "endLine": 67, + "tokens": 97 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/presentation/smtp/sasl_login.go": { + "description": "loginServer (15 fields); methods: Next", + "tokens": 561, + "hash": "6d9d2d4aa636dfcd", + "size": 1963, + "mtimeMs": 1785635482529.9143, + "updatedAt": "2026-10-06T11:06:20.893Z", + "source": "scan", + "symbols": [ + { + "name": "loginAuthenticator", + "kind": "class", + "startLine": 24, + "endLine": 24, + "tokens": 18 + }, + { + "name": "loginServer", + "kind": "class", + "startLine": 27, + "endLine": 33, + "tokens": 52 + }, + { + "name": "newLoginServer", + "kind": "fn", + "startLine": 41, + "endLine": 43, + "tokens": 34 + }, + { + "name": "Next", + "kind": "method", + "startLine": 45, + "endLine": 67, + "tokens": 205 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/protocols/internal/presentation/smtp/submission_backend_test.go": { + "description": "TestSubmissionSession_RefusesCommandsBeforeAuth, TestSubmissionSession_RefusesForeignSender, TestSubmissionSession_AdvertisesPlainAndLogin, TestSub...", + "tokens": 816, + "hash": "f70b36b321879ae7", + "size": 2853, + "mtimeMs": 1785635482529.9143, + "updatedAt": "2026-10-06T11:06:20.893Z", + "source": "scan", + "symbols": [ + { + "name": "smtpCode", + "kind": "fn", + "startLine": 13, + "endLine": 23, + "tokens": 63 + }, + { + "name": "ok", + "kind": "fn", + "startLine": 25, + "endLine": 31, + "tokens": 36 + }, + { + "name": "TestSubmissionSession_RefusesCommandsBeforeAuth", + "kind": "fn", + "startLine": 35, + "endLine": 47, + "tokens": 138 + }, + { + "name": "TestSubmissionSession_RefusesForeignSender", + "kind": "fn", + "startLine": 50, + "endLine": 59, + "tokens": 117 + }, + { + "name": "TestSubmissionSession_AdvertisesPlainAndLogin", + "kind": "fn", + "startLine": 61, + "endLine": 68, + "tokens": 86 + }, + { + "name": "TestSubmissionSession_RejectsUnsupportedMechanism", + "kind": "fn", + "startLine": 70, + "endLine": 74, + "tokens": 58 + }, + { + "name": "TestSubmissionSession_ResetKeepsAuthentication", + "kind": "fn", + "startLine": 78, + "endLine": 91, + "tokens": 128 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/application/usecase/apply_rules_test.go" + ], + "importance": 0.0144 + }, + "services/protocols/internal/presentation/smtp/submission_backend.go": { + "description": "SubmissionBackend (29 fields); methods: NewSession, AuthMechanisms, Auth, Reset", + "tokens": 1300, + "hash": "25672eb00b73a8af", + "size": 4547, + "mtimeMs": 1785635482529.9143, + "updatedAt": "2026-10-06T11:06:20.893Z", + "source": "scan", + "symbols": [ + { + "name": "SubmissionBackend", + "kind": "class", + "startLine": 19, + "endLine": 21, + "tokens": 23 + }, + { + "name": "NewSubmissionBackend", + "kind": "fn", + "startLine": 23, + "endLine": 25, + "tokens": 37 + }, + { + "name": "NewSession", + "kind": "method", + "startLine": 27, + "endLine": 29, + "tokens": 39 + }, + { + "name": "submissionSession", + "kind": "class", + "startLine": 31, + "endLine": 37, + "tokens": 68 + }, + { + "name": "AuthMechanisms", + "kind": "method", + "startLine": 43, + "endLine": 45, + "tokens": 30 + }, + { + "name": "Auth", + "kind": "method", + "startLine": 47, + "endLine": 58, + "tokens": 102 + }, + { + "name": "authenticate", + "kind": "method", + "startLine": 60, + "endLine": 71, + "tokens": 105 + }, + { + "name": "Reset", + "kind": "method", + "startLine": 73, + "endLine": 76, + "tokens": 21 + }, + { + "name": "Logout", + "kind": "method", + "startLine": 78, + "endLine": 81, + "tokens": 24 + }, + { + "name": "Mail", + "kind": "method", + "startLine": 83, + "endLine": 96, + "tokens": 119 + }, + { + "name": "Rcpt", + "kind": "method", + "startLine": 98, + "endLine": 104, + "tokens": 54 + }, + { + "name": "Data", + "kind": "method", + "startLine": 106, + "endLine": 145, + "tokens": 343 + }, + { + "name": "errAuthRequired", + "kind": "fn", + "startLine": 147, + "endLine": 153, + "tokens": 50 + } + ], + "symbolSource": "ts", + "imports": [ + "services/protocols/internal/application/port/arc_sealer.go", + "services/protocols/internal/application/usecase/apply_rules_test.go" + ], + "importance": 0.0144 + }, + "services/rspamd/Dockerfile": { + "description": "Docker container definition", + "tokens": 147, + "hash": "a2e8ec19787ff7a9", + "size": 551, + "mtimeMs": 1785692590854.0117, + "updatedAt": "2026-10-06T11:06:20.893Z", + "source": "scan", + "importance": 0.0144 + }, + "services/storage/Dockerfile": { + "description": "Docker container definition", + "tokens": 466, + "hash": "392c92dedbe9ff49", + "size": 1747, + "mtimeMs": 1785690291764.3303, + "updatedAt": "2026-10-06T11:06:20.893Z", + "source": "scan", + "importance": 0.0144 + }, + "services/storage/package.json": { + "description": "Node.js package manifest", + "tokens": 131, + "hash": "31d1cc1bbc07f421", + "size": 457, + "mtimeMs": 1785689161519.9346, + "updatedAt": "2026-10-06T11:06:20.894Z", + "source": "scan", + "importance": 0.0144 + }, + "services/storage/src/health.test.ts": { + "description": "Declares withServer", + "tokens": 470, + "hash": "02929edf5bd0abf7", + "size": 1645, + "mtimeMs": 1785635482530.1213, + "updatedAt": "2026-10-06T11:06:20.894Z", + "source": "scan", + "imports": [ + "services/storage/src/health.ts" + ], + "importance": 0.0144 + }, + "services/storage/src/health.ts": { + "description": "Builds the /health/live and /health/ready handler shared by the auth and", + "tokens": 215, + "hash": "4e0751ac405a206c", + "size": 751, + "mtimeMs": 1785635482530.289, + "updatedAt": "2026-10-06T11:06:20.894Z", + "source": "scan", + "importance": 0.0389 + }, + "services/storage/src/server.ts": { + "description": "Declares port", + "tokens": 212, + "hash": "d14480f8f8519ba0", + "size": 742, + "mtimeMs": 1785635482530.289, + "updatedAt": "2026-10-06T11:06:20.894Z", + "source": "scan", + "imports": [ + "services/storage/src/health.ts" + ], + "importance": 0.0144 + }, + "services/storage/tsconfig.build.json": { + "description": "TypeScript build configuration", + "tokens": 83, + "hash": "a657ee6c4c15f62f", + "size": 289, + "mtimeMs": 1785689161519.9824, + "updatedAt": "2026-10-06T11:06:20.894Z", + "source": "scan", + "importance": 0.0144 + }, + "services/storage/tsconfig.json": { + "description": "TypeScript configuration", + "tokens": 88, + "hash": "f5afcc0c2ccf20a7", + "size": 308, + "mtimeMs": 1785635482530.289, + "updatedAt": "2026-10-06T11:06:20.894Z", + "source": "scan", + "importance": 0.0144 + }, + "services/storage/vitest.config.ts": { + "description": "Vitest test configuration", + "tokens": 33, + "hash": "c0861bcf5235fb98", + "size": 114, + "mtimeMs": 1785635482530.289, + "updatedAt": "2026-10-06T11:06:20.894Z", + "source": "scan", + "importance": 0.0144 + }, + "services/webmail/Dockerfile": { + "description": "Docker container definition", + "tokens": 395, + "hash": "debbac0482d69e4e", + "size": 1479, + "mtimeMs": 1785690291764.4133, + "updatedAt": "2026-10-06T11:06:20.894Z", + "source": "scan", + "importance": 0.0144 + }, + "services/webmail/messages/en.json": { + "description": "", + "tokens": 5338, + "hash": "c12d893b92766aeb", + "size": 18688, + "mtimeMs": 1788830725028.8906, + "updatedAt": "2026-10-06T11:06:20.895Z", + "source": "scan", + "importance": 0.0291 + }, + "services/webmail/messages/es.json": { + "description": "", + "tokens": 5774, + "hash": "cb137d35b48eb257", + "size": 20371, + "mtimeMs": 1788830725029.1912, + "updatedAt": "2026-10-06T11:06:20.895Z", + "source": "scan", + "importance": 0.0291 + }, + "services/webmail/messages/pt-BR.json": { + "description": "", + "tokens": 5763, + "hash": "0fe6821705dd9474", + "size": 20406, + "mtimeMs": 1788830725029.4724, + "updatedAt": "2026-10-06T11:06:20.895Z", + "source": "scan", + "importance": 0.0291 + }, + "services/webmail/next-env.d.ts": { + "description": "/ ", + "tokens": 75, + "hash": "85ae5aee75f01196", + "size": 262, + "mtimeMs": 1785635482530.4248, + "updatedAt": "2026-10-06T11:06:20.895Z", + "source": "scan", + "importance": 0.0144 + }, + "services/webmail/next.config.ts": { + "description": "Next.js configuration", + "tokens": 601, + "hash": "c7639b86195e8a53", + "size": 2103, + "mtimeMs": 1788831584456.196, + "updatedAt": "2026-10-06T11:06:20.895Z", + "source": "scan", + "importance": 0.0144 + }, + "services/webmail/package.json": { + "description": "Node.js package manifest", + "tokens": 263, + "hash": "b68d6253df12f339", + "size": 920, + "mtimeMs": 1787396918822.9626, + "updatedAt": "2026-10-06T11:06:20.895Z", + "source": "scan", + "importance": 0.0144 + }, + "services/webmail/postcss.config.js": { + "description": "PostCSS configuration", + "tokens": 24, + "hash": "fa650b380adfabb1", + "size": 83, + "mtimeMs": 1785694432297.9165, + "updatedAt": "2026-10-06T11:06:20.895Z", + "source": "scan", + "importance": 0.0144 + }, + "services/webmail/public/.gitkeep": { + "description": "", + "tokens": 0, + "hash": "e3b0c44298fc1c14", + "size": 0, + "mtimeMs": 1785635482530.4248, + "updatedAt": "2026-10-06T11:06:20.895Z", + "source": "scan", + "importance": 0.0144 + }, + "services/webmail/src/app/(admin)/admin/dashboard/page.tsx": { + "description": "Exactly the fields /api/v1/admin/dashboard returns - see dashboardStats().", + "tokens": 2425, + "hash": "6c61c9c191907e15", + "size": 8487, + "mtimeMs": 1787094683115.5483, + "updatedAt": "2026-10-06T11:06:20.896Z", + "source": "scan", + "symbols": [ + { + "name": "DashboardMetrics", + "kind": "section", + "startLine": 21, + "endLine": 31, + "tokens": 76 + }, + { + "name": "formatBytes", + "kind": "fn", + "startLine": 33, + "endLine": 38, + "tokens": 80 + }, + { + "name": "AdminDashboardPage", + "kind": "fn", + "startLine": 40, + "endLine": 215, + "tokens": 2127 + } + ], + "symbolSource": "ts", + "skeleton": "interface DashboardMetrics {\nfunction formatBytes(bytes: number): string {\nexport default function AdminDashboardPage() {", + "imports": [ + "services/webmail/src/i18n/provider.tsx", + "services/webmail/src/components/ui/Badge.tsx", + "services/webmail/src/components/ui/Button.tsx" + ], + "importance": 0.0144 + }, + "services/webmail/src/app/(admin)/admin/dmarc/page.tsx": { + "description": "AdminDmarcPage β€” renders table β€” uses useEffect", + "tokens": 1215, + "hash": "4052e9e85caecf5c", + "size": 4251, + "mtimeMs": 1787094950774.6467, + "updatedAt": "2026-10-06T11:06:20.896Z", + "source": "scan", + "symbols": [ + { + "name": "DmarcSource", + "kind": "section", + "startLine": 7, + "endLine": 13, + "tokens": 30 + }, + { + "name": "DmarcData", + "kind": "section", + "startLine": 15, + "endLine": 21, + "tokens": 45 + }, + { + "name": "AdminDmarcPage", + "kind": "fn", + "startLine": 23, + "endLine": 106, + "tokens": 1087 + } + ], + "symbolSource": "ts", + "imports": [ + "services/webmail/src/i18n/provider.tsx", + "services/webmail/src/components/ui/Badge.tsx" + ], + "importance": 0.0297 + }, + "services/webmail/src/app/(admin)/admin/domains/page.tsx": { + "description": "Domains, read from the database.", + "tokens": 7639, + "hash": "f833986ee83dd321", + "size": 26734, + "mtimeMs": 1788830725029.8057, + "updatedAt": "2026-10-06T11:06:20.897Z", + "source": "scan", + "symbols": [ + { + "name": "DnsRecordCheck", + "kind": "section", + "startLine": 26, + "endLine": 33, + "tokens": 40 + }, + { + "name": "ReconcileResult", + "kind": "section", + "startLine": 35, + "endLine": 42, + "tokens": 44 + }, + { + "name": "DnsVerification", + "kind": "section", + "startLine": 44, + "endLine": 50, + "tokens": 38 + }, + { + "name": "Domain", + "kind": "section", + "startLine": 52, + "endLine": 62, + "tokens": 70 + }, + { + "name": "statusVariant", + "kind": "fn", + "startLine": 64, + "endLine": 68, + "tokens": 61 + }, + { + "name": "AdminDomainsPage", + "kind": "fn", + "startLine": 70, + "endLine": 657, + "tokens": 7033 + } + ], + "symbolSource": "ts", + "skeleton": "interface DnsRecordCheck {\ninterface ReconcileResult {\ninterface DnsVerification {\ninterface Domain {\nfunction statusVariant(status: string): \"success\" | \"warning\" | \"danger\" {\nexport default function AdminDomainsPage() {", + "imports": [ + "services/webmail/src/i18n/provider.tsx", + "services/webmail/src/components/ui/Badge.tsx", + "services/webmail/src/components/ui/Button.tsx" + ], + "importance": 0.0144 + }, + "services/webmail/src/app/(admin)/admin/layout.tsx": { + "description": "AdminLayout β€” uses useState", + "tokens": 2246, + "hash": "faad0c293fe00511", + "size": 7859, + "mtimeMs": 1787574908613.0957, + "updatedAt": "2026-10-06T11:06:20.897Z", + "source": "scan", + "symbols": [ + { + "name": "AdminLayout", + "kind": "fn", + "startLine": 26, + "endLine": 187, + "tokens": 2080 + } + ], + "symbolSource": "ts", + "skeleton": "export default function AdminLayout({ children }: { children: React.ReactNode }) {", + "imports": [ + "services/webmail/src/i18n/provider.tsx", + "services/webmail/src/i18n/LanguageSwitcher.tsx", + "services/webmail/src/lib/useAccount.ts", + "services/webmail/src/lib/initials.ts" + ], + "importance": 0.0144 + }, + "services/webmail/src/app/(admin)/admin/mailboxes/page.tsx": { + "description": "The envelope every server-paged list answers with.", + "tokens": 12626, + "hash": "1117ff9da8bfbe18", + "size": 44191, + "mtimeMs": 1787582496191.818, + "updatedAt": "2026-10-06T11:06:20.898Z", + "source": "scan", + "symbols": [ + { + "name": "Mailbox", + "kind": "section", + "startLine": 30, + "endLine": 39, + "tokens": 49 + }, + { + "name": "Alias", + "kind": "section", + "startLine": 41, + "endLine": 46, + "tokens": 29 + }, + { + "name": "ApiMailbox", + "kind": "section", + "startLine": 48, + "endLine": 57, + "tokens": 56 + }, + { + "name": "PagedUsers", + "kind": "section", + "startLine": 60, + "endLine": 66, + "tokens": 36 + }, + { + "name": "ApiAlias", + "kind": "section", + "startLine": 68, + "endLine": 73, + "tokens": 35 + }, + { + "name": "AdminMailboxesPage", + "kind": "fn", + "startLine": 75, + "endLine": 1101, + "tokens": 12184 + } + ], + "symbolSource": "ts", + "skeleton": "interface Mailbox {\ninterface Alias {\ninterface ApiMailbox {\ninterface PagedUsers {\ninterface ApiAlias {\nexport default function AdminMailboxesPage() {", + "imports": [ + "services/webmail/src/i18n/provider.tsx", + "services/webmail/src/components/ui/Badge.tsx", + "services/webmail/src/components/ui/Pagination.tsx", + "services/webmail/src/lib/useDebounced.ts", + "services/webmail/src/lib/formatBytes.ts", + "services/webmail/src/lib/roleLabel.ts", + "services/webmail/src/lib/initials.ts", + "services/webmail/src/components/ui/Button.tsx" + ], + "importance": 0.0144 + }, + "services/webmail/src/app/(admin)/admin/queue/page.tsx": { + "description": "The outbound queue, read from the database.", + "tokens": 2956, + "hash": "3235d0da7ecdbdf3", + "size": 10343, + "mtimeMs": 1787574075074.975, + "updatedAt": "2026-10-06T11:06:20.898Z", + "source": "scan", + "symbols": [ + { + "name": "QueueJob", + "kind": "section", + "startLine": 23, + "endLine": 33, + "tokens": 71 + }, + { + "name": "QueueSummary", + "kind": "section", + "startLine": 35, + "endLine": 42, + "tokens": 47 + }, + { + "name": "statusVariant", + "kind": "fn", + "startLine": 51, + "endLine": 55, + "tokens": 61 + }, + { + "name": "AdminQueuePage", + "kind": "fn", + "startLine": 57, + "endLine": 252, + "tokens": 2404 + } + ], + "symbolSource": "ts", + "skeleton": "interface QueueJob {\ninterface QueueSummary {\nfunction statusVariant(status: string): \"danger\" | \"warning\" | \"neutral\" {\nexport default function AdminQueuePage() {", + "imports": [ + "services/webmail/src/i18n/provider.tsx", + "services/webmail/src/components/ui/Badge.tsx", + "services/webmail/src/components/ui/Button.tsx", + "services/webmail/src/components/ui/Pagination.tsx", + "services/webmail/src/lib/useDebounced.ts" + ], + "importance": 0.0144 + }, + "services/webmail/src/app/(admin)/admin/security/page.tsx": { + "description": "Live certificate state from the protocols service, which owns the cert", + "tokens": 6267, + "hash": "8dff5cbdcb6d7948", + "size": 21933, + "mtimeMs": 1788275287930.1296, + "updatedAt": "2026-10-06T11:06:20.898Z", + "source": "scan", + "symbols": [ + { + "name": "PreflightCheck", + "kind": "section", + "startLine": 18, + "endLine": 24, + "tokens": 40 + }, + { + "name": "ApiAuditRow", + "kind": "section", + "startLine": 26, + "endLine": 34, + "tokens": 60 + }, + { + "name": "AuditEntry", + "kind": "section", + "startLine": 36, + "endLine": 44, + "tokens": 42 + }, + { + "name": "AdminSecurityPage", + "kind": "fn", + "startLine": 46, + "endLine": 528, + "tokens": 6018 + } + ], + "symbolSource": "ts", + "skeleton": "interface PreflightCheck {\ninterface ApiAuditRow {\ninterface AuditEntry {\nexport default function AdminSecurityPage() {", + "imports": [ + "services/webmail/src/i18n/provider.tsx", + "services/webmail/src/components/ui/Badge.tsx", + "services/webmail/src/components/ui/Button.tsx" + ], + "importance": 0.0144 + }, + "services/webmail/src/app/(admin)/admin/tests/page.tsx": { + "description": "Checks an operator runs by hand, against the server or one user.", + "tokens": 2274, + "hash": "8aee7e658be6d0bf", + "size": 7958, + "mtimeMs": 1787575315828.4277, + "updatedAt": "2026-10-06T11:06:20.898Z", + "source": "scan", + "symbols": [ + { + "name": "Check", + "kind": "section", + "startLine": 17, + "endLine": 22, + "tokens": 34 + }, + { + "name": "Report", + "kind": "section", + "startLine": 24, + "endLine": 29, + "tokens": 30 + }, + { + "name": "UserRow", + "kind": "section", + "startLine": 31, + "endLine": 34, + "tokens": 18 + }, + { + "name": "variantFor", + "kind": "fn", + "startLine": 36, + "endLine": 41, + "tokens": 70 + }, + { + "name": "AdminTestsPage", + "kind": "fn", + "startLine": 43, + "endLine": 217, + "tokens": 1942 + } + ], + "symbolSource": "ts", + "skeleton": "interface Check {\ninterface Report {\ninterface UserRow {\nfunction variantFor(status: Check[\"status\"]): \"success\" | \"warning\" | \"danger\" | \"neutral\" {\nexport default function AdminTestsPage() {", + "imports": [ + "services/webmail/src/i18n/provider.tsx", + "services/webmail/src/components/ui/Badge.tsx", + "services/webmail/src/components/ui/Button.tsx", + "services/webmail/src/lib/useDebounced.ts" + ], + "importance": 0.0144 + }, + "services/webmail/src/app/(auth)/admin/login/page.tsx": { + "description": "AdminLoginPage β€” renders form β€” uses useState", + "tokens": 2803, + "hash": "f6bcbf5b4c47e083", + "size": 9809, + "mtimeMs": 1787095819933.3389, + "updatedAt": "2026-10-06T11:06:20.899Z", + "source": "scan", + "symbols": [ + { + "name": "AdminLoginPage", + "kind": "fn", + "startLine": 9, + "endLine": 238, + "tokens": 2719 + } + ], + "symbolSource": "ts", + "skeleton": "export default function AdminLoginPage() {", + "imports": [ + "services/webmail/src/i18n/provider.tsx", + "services/webmail/src/components/TotpEnrolment.tsx", + "services/webmail/src/components/ui/Button.tsx" + ], + "importance": 0.0144 + }, + "services/webmail/src/app/(auth)/admin/step-up/page.tsx": { + "description": "Crossing from the webmail into the console.", + "tokens": 2187, + "hash": "65d065b69def6393", + "size": 7654, + "mtimeMs": 1787095198167.7102, + "updatedAt": "2026-10-06T11:06:20.899Z", + "source": "scan", + "symbols": [ + { + "name": "AdminStepUpPage", + "kind": "fn", + "startLine": 22, + "endLine": 178, + "tokens": 1927 + } + ], + "symbolSource": "ts", + "skeleton": "export default function AdminStepUpPage() {", + "imports": [ + "services/webmail/src/i18n/provider.tsx", + "services/webmail/src/lib/useAccount.ts", + "services/webmail/src/components/TotpEnrolment.tsx", + "services/webmail/src/components/ui/Button.tsx" + ], + "importance": 0.0144 + }, + "services/webmail/src/app/(auth)/user/login/page.tsx": { + "description": "One step, because the mailbox is one step.", + "tokens": 1486, + "hash": "9fc2b90531939ed0", + "size": 5200, + "mtimeMs": 1787095819933.482, + "updatedAt": "2026-10-06T11:06:20.900Z", + "source": "scan", + "symbols": [ + { + "name": "UserLoginPage", + "kind": "fn", + "startLine": 8, + "endLine": 125, + "tokens": 1419 + } + ], + "symbolSource": "ts", + "imports": [ + "services/webmail/src/i18n/provider.tsx", + "services/webmail/src/components/ui/Button.tsx" + ], + "importance": 0.0144 + }, + "services/webmail/src/app/(user)/user/compose/page.tsx": { + "description": "Splits a comma or semicolon separated field, dropping empty entries.", + "tokens": 5433, + "hash": "c5002903c42eec20", + "size": 19015, + "mtimeMs": 1787414872252.8132, + "updatedAt": "2026-10-06T11:06:20.900Z", + "source": "scan", + "symbols": [ + { + "name": "splitAddresses", + "kind": "fn", + "startLine": 25, + "endLine": 30, + "tokens": 41 + }, + { + "name": "AttachedFile", + "kind": "section", + "startLine": 32, + "endLine": 37, + "tokens": 26 + }, + { + "name": "ComposePage", + "kind": "fn", + "startLine": 39, + "endLine": 483, + "tokens": 5204 + } + ], + "symbolSource": "ts", + "skeleton": "function splitAddresses(value: string): string[] {\ninterface AttachedFile {\nexport default function ComposePage({", + "imports": [ + "services/webmail/src/i18n/provider.tsx", + "services/webmail/src/components/ui/Button.tsx", + "services/webmail/src/lib/signature.ts" + ], + "importance": 0.0144 + }, + "services/webmail/src/app/(user)/user/layout.tsx": { + "description": "Creates, renames or deletes one of the user's own folders.", + "tokens": 4684, + "hash": "6fe7f0a83a00b89b", + "size": 16393, + "mtimeMs": 1788830725030.1267, + "updatedAt": "2026-10-06T11:06:20.900Z", + "source": "scan", + "symbols": [ + { + "name": "UserWebmailLayout", + "kind": "fn", + "startLine": 32, + "endLine": 370, + "tokens": 4459 + } + ], + "symbolSource": "ts", + "skeleton": "export default function UserWebmailLayout({ children }: { children: React.ReactNode }) {", + "imports": [ + "services/webmail/src/i18n/provider.tsx", + "services/webmail/src/i18n/LanguageSwitcher.tsx", + "services/webmail/src/lib/useAccount.ts", + "services/webmail/src/lib/useFolders.ts", + "services/webmail/src/lib/mailCounts.ts", + "services/webmail/src/components/Avatar.tsx" + ], + "importance": 0.0144 + }, + "services/webmail/src/app/(user)/user/mail/[folder]/page.tsx": { + "description": "The initials shown in the sender avatar, from a display name or an address.", + "tokens": 10230, + "hash": "ee7ca55047adb9c5", + "size": 35802, + "mtimeMs": 1788830725030.614, + "updatedAt": "2026-10-06T11:06:20.902Z", + "source": "scan", + "symbols": [ + { + "name": "MessageSummary", + "kind": "section", + "startLine": 43, + "endLine": 55, + "tokens": 79 + }, + { + "name": "RenderedMessage", + "kind": "section", + "startLine": 57, + "endLine": 70, + "tokens": 105 + }, + { + "name": "initials", + "kind": "fn", + "startLine": 73, + "endLine": 78, + "tokens": 85 + }, + { + "name": "listTimestamp", + "kind": "fn", + "startLine": 85, + "endLine": 96, + "tokens": 138 + }, + { + "name": "formatBytes", + "kind": "fn", + "startLine": 98, + "endLine": 108, + "tokens": 99 + }, + { + "name": "MailFolderPage", + "kind": "fn", + "startLine": 110, + "endLine": 800, + "tokens": 9288 + } + ], + "symbolSource": "ts", + "skeleton": "interface MessageSummary {\ninterface RenderedMessage {\nfunction initials(name: string, address: string): string {\nfunction listTimestamp(iso: string, locale: string): string {\nfunction formatBytes(bytes: number): string {\nexport default function MailFolderPage({ params }: { params: Promise<{ folder: string }> }) {", + "imports": [ + "services/webmail/src/i18n/provider.tsx", + "services/webmail/src/lib/useMailEvents.ts", + "services/webmail/src/lib/useFolders.ts", + "services/webmail/src/lib/sanitizer.ts", + "services/webmail/src/lib/remoteImages.ts", + "services/webmail/src/lib/emailBody.ts", + "services/webmail/src/components/ui/Button.tsx", + "services/webmail/src/components/Avatar.tsx" + ], + "importance": 0.0644 + }, + "services/webmail/src/app/(user)/user/settings/page.tsx": { + "description": "Writes the whole rule list to the server as one script.", + "tokens": 12580, + "hash": "f6ef92503b6039d9", + "size": 44030, + "mtimeMs": 1788830725030.992, + "updatedAt": "2026-10-06T11:06:20.903Z", + "source": "scan", + "symbols": [ + { + "name": "SieveRule", + "kind": "section", + "startLine": 32, + "endLine": 39, + "tokens": 62 + }, + { + "name": "AppPassword", + "kind": "section", + "startLine": 41, + "endLine": 46, + "tokens": 32 + }, + { + "name": "WebSession", + "kind": "section", + "startLine": 48, + "endLine": 55, + "tokens": 45 + }, + { + "name": "SettingsTab", + "kind": "section", + "startLine": 57, + "endLine": 57, + "tokens": 27 + }, + { + "name": "UserSettingsPage", + "kind": "fn", + "startLine": 59, + "endLine": 994, + "tokens": 12113 + } + ], + "symbolSource": "ts", + "skeleton": "interface SieveRule {\ninterface AppPassword {\ninterface WebSession {\ntype SettingsTab = \"profile\" | \"security\" | \"filters\" | \"vacation\" | \"signature\" | \"language\";\nexport default function UserSettingsPage() {", + "imports": [ + "services/webmail/src/i18n/provider.tsx", + "services/webmail/src/components/ui/Badge.tsx", + "services/webmail/src/components/ui/Button.tsx", + "services/webmail/src/components/TotpEnrolment.tsx", + "services/webmail/src/i18n/LanguageSwitcher.tsx", + "services/webmail/src/lib/signature.ts", + "services/webmail/src/lib/rules.ts", + "services/webmail/src/lib/useFolders.ts", + "services/webmail/src/components/AvatarEditor.tsx", + "services/webmail/src/lib/useAccount.ts", + "services/webmail/src/lib/mailCounts.ts" + ], + "importance": 0.0144 + }, + "services/webmail/src/app/api/v1/[...path]/route.ts": { + "description": "Proxies /api/v1/* to the auth service.", + "tokens": 1250, + "hash": "af2cc07c4c34d1f0", + "size": 4373, + "mtimeMs": 1788831584455.9814, + "updatedAt": "2026-10-06T11:06:20.903Z", + "source": "scan", + "symbols": [ + { + "name": "upstreamFor", + "kind": "fn", + "startLine": 33, + "endLine": 37, + "tokens": 65 + }, + { + "name": "filterHeaders", + "kind": "fn", + "startLine": 54, + "endLine": 60, + "tokens": 58 + }, + { + "name": "forward", + "kind": "fn", + "startLine": 62, + "endLine": 108, + "tokens": 498 + }, + { + "name": "Context", + "kind": "section", + "startLine": 110, + "endLine": 110, + "tokens": 16 + }, + { + "name": "handler", + "kind": "fn", + "startLine": 112, + "endLine": 115, + "tokens": 43 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/webmail/src/app/globals.css": { + "description": "Nocturne - the one visual language for both surfaces.", + "tokens": 2171, + "hash": "446efc6c471e5b66", + "size": 7596, + "mtimeMs": 1787095819934.1978, + "updatedAt": "2026-10-06T11:06:20.903Z", + "source": "scan", + "importance": 0.0185 + }, + "services/webmail/src/app/health/route.ts": { + "description": "Liveness probe for the web container.", + "tokens": 210, + "hash": "9bdb05ee9eb9f3a9", + "size": 735, + "mtimeMs": 1785708839136.4302, + "updatedAt": "2026-10-06T11:06:20.903Z", + "source": "scan", + "importance": 0.0144 + }, + "services/webmail/src/app/layout.tsx": { + "description": "The locale is resolved on the server so the first paint is already in the", + "tokens": 356, + "hash": "1df421ef6ab67c00", + "size": 1243, + "mtimeMs": 1787094274325.3748, + "updatedAt": "2026-10-06T11:06:20.903Z", + "source": "scan", + "imports": [ + "services/webmail/src/app/globals.css", + "services/webmail/src/i18n/provider.tsx", + "services/webmail/src/i18n/config.ts" + ], + "importance": 0.0144 + }, + "services/webmail/src/app/page.tsx": { + "description": "The root sends everyone straight into the mail client.", + "tokens": 213, + "hash": "335452fc8be98fd9", + "size": 743, + "mtimeMs": 1785708839136.5457, + "updatedAt": "2026-10-06T11:06:20.903Z", + "source": "scan", + "importance": 0.0144 + }, + "services/webmail/src/components/Avatar.tsx": { + "description": "Rendered size in pixels; the square the circle is cut from.", + "tokens": 542, + "hash": "37e36f109056e098", + "size": 1897, + "mtimeMs": 1788830725031.2024, + "updatedAt": "2026-10-06T11:06:20.904Z", + "source": "scan", + "symbols": [ + { + "name": "AvatarProps", + "kind": "section", + "startLine": 6, + "endLine": 17, + "tokens": 119 + }, + { + "name": "Avatar", + "kind": "fn", + "startLine": 27, + "endLine": 54, + "tokens": 290 + } + ], + "symbolSource": "ts", + "imports": [ + "services/webmail/src/lib/initials.ts" + ], + "importance": 0.0233 + }, + "services/webmail/src/components/AvatarEditor.tsx": { + "description": "The frame is square; the mask over it is what makes the photo a circle.", + "tokens": 2510, + "hash": "a84b46464d32f1a7", + "size": 8783, + "mtimeMs": 1788830725031.433, + "updatedAt": "2026-10-06T11:06:20.904Z", + "source": "scan", + "symbols": [ + { + "name": "AvatarEditorProps", + "kind": "section", + "startLine": 21, + "endLine": 25, + "tokens": 34 + }, + { + "name": "AvatarEditor", + "kind": "fn", + "startLine": 27, + "endLine": 254, + "tokens": 2309 + } + ], + "symbolSource": "ts", + "skeleton": "interface AvatarEditorProps {\nexport function AvatarEditor({ src, onSaved }: AvatarEditorProps) {", + "imports": [ + "services/webmail/src/components/ui/Button.tsx", + "services/webmail/src/i18n/provider.tsx" + ], + "importance": 0.0155 + }, + "services/webmail/src/components/TotpEnrolment.tsx": { + "description": "The scannable half of TOTP enrolment, shared by the webmail settings page and", + "tokens": 1644, + "hash": "2bf14f265d7d3c26", + "size": 5753, + "mtimeMs": 1787095096887.0015, + "updatedAt": "2026-10-06T11:06:20.904Z", + "source": "scan", + "symbols": [ + { + "name": "qrDataUri", + "kind": "fn", + "startLine": 28, + "endLine": 43, + "tokens": 178 + }, + { + "name": "TotpEnrolment", + "kind": "fn", + "startLine": 45, + "endLine": 97, + "tokens": 545 + }, + { + "name": "RecoveryCodes", + "kind": "fn", + "startLine": 106, + "endLine": 160, + "tokens": 517 + } + ], + "symbolSource": "ts", + "imports": [ + "services/webmail/src/i18n/provider.tsx" + ], + "importance": 0.0227 + }, + "services/webmail/src/components/ui/Badge.tsx": { + "description": "A tag: a small tinted label from the ramps.", + "tokens": 375, + "hash": "13ef6bc464034894", + "size": 1312, + "mtimeMs": 1787094255667.1128, + "updatedAt": "2026-10-06T11:06:20.904Z", + "source": "scan", + "importance": 0.0474 + }, + "services/webmail/src/components/ui/Button.tsx": { + "description": "Nocturne buttons are outlined, not filled.", + "tokens": 584, + "hash": "299744ec90898e32", + "size": 2043, + "mtimeMs": 1787094233363.3872, + "updatedAt": "2026-10-06T11:06:20.904Z", + "source": "scan", + "symbols": [ + { + "name": "ButtonProps", + "kind": "section", + "startLine": 5, + "endLine": 9, + "tokens": 60 + }, + { + "name": "Button", + "kind": "fn", + "startLine": 21, + "endLine": 58, + "tokens": 366 + } + ], + "symbolSource": "ts", + "importance": 0.0708 + }, + "services/webmail/src/components/ui/Card.tsx": { + "description": "A panel: a solid ground with a hairline inset ring.", + "tokens": 421, + "hash": "3b7f8d69aa21e3b7", + "size": 1473, + "mtimeMs": 1787094244106.6357, + "updatedAt": "2026-10-06T11:06:20.904Z", + "source": "scan", + "importance": 0.0144 + }, + "services/webmail/src/components/ui/Pagination.tsx": { + "description": "The pager under a server-paged list.", + "tokens": 672, + "hash": "eb5628b661c65a20", + "size": 2349, + "mtimeMs": 1787574044769.0635, + "updatedAt": "2026-10-06T11:06:20.904Z", + "source": "scan", + "symbols": [ + { + "name": "PaginationProps", + "kind": "section", + "startLine": 9, + "endLine": 16, + "tokens": 45 + }, + { + "name": "Pagination", + "kind": "fn", + "startLine": 24, + "endLine": 76, + "tokens": 497 + } + ], + "symbolSource": "ts", + "imports": [ + "services/webmail/src/components/ui/Button.tsx", + "services/webmail/src/lib/paging.ts", + "services/webmail/src/i18n/provider.tsx" + ], + "importance": 0.0184 + }, + "services/webmail/src/compose-draft-integrity.test.ts": { + "description": "The composer's draft handling, and the delete action the mail list was", + "tokens": 1189, + "hash": "3ea72051be9266c4", + "size": 4159, + "mtimeMs": 1787315464690.8062, + "updatedAt": "2026-10-06T11:06:20.904Z", + "source": "scan", + "symbols": [ + { + "name": "read", + "kind": "fn", + "startLine": 14, + "endLine": 14, + "tokens": 24 + }, + { + "name": "code", + "kind": "fn", + "startLine": 15, + "endLine": 19, + "tokens": 46 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/webmail/src/emailBody.test.ts": { + "description": "The reader pane showed broken image icons for every message written by a", + "tokens": 1334, + "hash": "aa2316ccca1dad2a", + "size": 4668, + "mtimeMs": 1787075616726.684, + "updatedAt": "2026-10-06T11:06:20.904Z", + "source": "scan", + "imports": [ + "services/webmail/src/lib/sanitizer.ts", + "services/webmail/src/lib/emailBody.ts" + ], + "importance": 0.0144 + }, + "services/webmail/src/i18n/config.ts": { + "description": "Picks the best supported locale from an Accept-Language header.", + "tokens": 993, + "hash": "cc076614d1df0b7b", + "size": 3473, + "mtimeMs": 1785708839136.8157, + "updatedAt": "2026-10-06T11:06:20.904Z", + "source": "scan", + "symbols": [ + { + "name": "Locale", + "kind": "section", + "startLine": 6, + "endLine": 6, + "tokens": 14 + }, + { + "name": "Messages", + "kind": "section", + "startLine": 28, + "endLine": 28, + "tokens": 18 + }, + { + "name": "isLocale", + "kind": "fn", + "startLine": 30, + "endLine": 32, + "tokens": 42 + }, + { + "name": "negotiateLocale", + "kind": "fn", + "startLine": 39, + "endLine": 59, + "tokens": 214 + }, + { + "name": "TranslateParams", + "kind": "section", + "startLine": 62, + "endLine": 62, + "tokens": 18 + }, + { + "name": "interpolate", + "kind": "fn", + "startLine": 71, + "endLine": 76, + "tokens": 76 + }, + { + "name": "translate", + "kind": "fn", + "startLine": 86, + "endLine": 92, + "tokens": 114 + } + ], + "symbolSource": "ts", + "imports": [ + "services/webmail/messages/en.json", + "services/webmail/messages/pt-BR.json", + "services/webmail/messages/es.json" + ], + "importance": 0.0518 + }, + "services/webmail/src/i18n/LanguageSwitcher.tsx": { + "description": "Language selector, rendered in both surface layouts (PLAN.md section 21.2).", + "tokens": 275, + "hash": "8557766e93e2e4f0", + "size": 962, + "mtimeMs": 1787094266144.591, + "updatedAt": "2026-10-06T11:06:20.904Z", + "source": "scan", + "imports": [ + "services/webmail/src/i18n/config.ts", + "services/webmail/src/i18n/provider.tsx" + ], + "importance": 0.0206 + }, + "services/webmail/src/i18n/provider.tsx": { + "description": "Shorthand for components that only need to translate.", + "tokens": 598, + "hash": "bdc5a710ef7b7826", + "size": 2093, + "mtimeMs": 1785708839136.924, + "updatedAt": "2026-10-06T11:06:20.904Z", + "source": "scan", + "symbols": [ + { + "name": "TranslateFn", + "kind": "section", + "startLine": 13, + "endLine": 13, + "tokens": 22 + }, + { + "name": "I18nContextValue", + "kind": "section", + "startLine": 15, + "endLine": 19, + "tokens": 30 + }, + { + "name": "I18nProvider", + "kind": "fn", + "startLine": 23, + "endLine": 48, + "tokens": 368 + }, + { + "name": "useI18n", + "kind": "fn", + "startLine": 50, + "endLine": 56, + "tokens": 53 + }, + { + "name": "useTranslations", + "kind": "fn", + "startLine": 59, + "endLine": 61, + "tokens": 21 + } + ], + "symbolSource": "ts", + "importance": 0.1207 + }, + "services/webmail/src/i18n.test.ts": { + "description": "loadJson: getLeafKeys", + "tokens": 885, + "hash": "9803a416c5251145", + "size": 3096, + "mtimeMs": 1785689161522.3713, + "updatedAt": "2026-10-06T11:06:20.904Z", + "source": "scan", + "symbols": [ + { + "name": "loadJson", + "kind": "fn", + "startLine": 5, + "endLine": 8, + "tokens": 50 + }, + { + "name": "getLeafKeys", + "kind": "fn", + "startLine": 10, + "endLine": 22, + "tokens": 129 + } + ], + "symbolSource": "ts", + "imports": [ + "services/webmail/src/i18n/config.ts" + ], + "importance": 0.0144 + }, + "services/webmail/src/initials.test.ts": { + "description": "", + "tokens": 293, + "hash": "44ebbeeb700881fc", + "size": 1024, + "mtimeMs": 1787094359195.7754, + "updatedAt": "2026-10-06T11:06:20.905Z", + "source": "scan", + "imports": [ + "services/webmail/src/lib/initials.ts" + ], + "importance": 0.0144 + }, + "services/webmail/src/interface-integrity.test.ts": { + "description": "Guards the class of defect this interface kept shipping: text that cannot be", + "tokens": 2690, + "hash": "c5b1fef1ce7a4b03", + "size": 9415, + "mtimeMs": 1787495211161.3228, + "updatedAt": "2026-10-06T11:06:20.905Z", + "source": "scan", + "symbols": [ + { + "name": "sourceFiles", + "kind": "fn", + "startLine": 15, + "endLine": 26, + "tokens": 116 + }, + { + "name": "code", + "kind": "fn", + "startLine": 81, + "endLine": 85, + "tokens": 43 + } + ], + "symbolSource": "ts", + "skeleton": "function sourceFiles(): string[] {\nfunction code(file: string): string {", + "imports": [ + "services/webmail/src/i18n/config.ts" + ], + "importance": 0.0144 + }, + "services/webmail/src/lib/avatarCrop.test.ts": { + "description": "Declares VIEWPORT", + "tokens": 1187, + "hash": "f879c9b500c9ed41", + "size": 4152, + "mtimeMs": 1788830725031.6545, + "updatedAt": "2026-10-06T11:06:20.905Z", + "source": "scan", + "importance": 0.0144 + }, + "services/webmail/src/lib/avatarCrop.ts": { + "description": "The geometry behind the circular photo editor.", + "tokens": 1132, + "hash": "05bacd6843368846", + "size": 3960, + "mtimeMs": 1788830725031.8364, + "updatedAt": "2026-10-06T11:06:20.905Z", + "source": "scan", + "symbols": [ + { + "name": "Size", + "kind": "section", + "startLine": 11, + "endLine": 14, + "tokens": 18 + }, + { + "name": "Transform", + "kind": "section", + "startLine": 17, + "endLine": 23, + "tokens": 63 + }, + { + "name": "minimumScale", + "kind": "fn", + "startLine": 32, + "endLine": 44, + "tokens": 175 + }, + { + "name": "scaleRange", + "kind": "fn", + "startLine": 47, + "endLine": 50, + "tokens": 48 + }, + { + "name": "clampTransform", + "kind": "fn", + "startLine": 59, + "endLine": 72, + "tokens": 167 + }, + { + "name": "initialTransform", + "kind": "fn", + "startLine": 75, + "endLine": 77, + "tokens": 44 + }, + { + "name": "SourceRect", + "kind": "section", + "startLine": 86, + "endLine": 91, + "tokens": 27 + }, + { + "name": "sourceRect", + "kind": "fn", + "startLine": 93, + "endLine": 109, + "tokens": 159 + } + ], + "symbolSource": "ts", + "importance": 0.0144 + }, + "services/webmail/src/lib/emailBody.ts": { + "description": "Turns a message's sanitized HTML into something a frame can actually show.", + "tokens": 1181, + "hash": "1a3d68e111e32fce", + "size": 4132, + "mtimeMs": 1787075616726.9568, + "updatedAt": "2026-10-06T11:06:20.905Z", + "source": "scan", + "symbols": [ + { + "name": "InlineImages", + "kind": "section", + "startLine": 12, + "endLine": 12, + "tokens": 15 + }, + { + "name": "resolveInlineImages", + "kind": "fn", + "startLine": 21, + "endLine": 34, + "tokens": 168 + }, + { + "name": "buildReaderDocument", + "kind": "fn", + "startLine": 43, + "endLine": 97, + "tokens": 667 + } + ], + "symbolSource": "ts", + "importance": 0.0274 + }, + "services/webmail/src/lib/formatBytes.test.ts": { + "description": "", + "tokens": 434, + "hash": "1833f0c47f0302f7", + "size": 1516, + "mtimeMs": 1787579724171.6265, + "updatedAt": "2026-10-06T11:06:20.905Z", + "source": "scan", + "imports": [ + "services/webmail/src/lib/formatBytes.ts" + ], + "importance": 0.0144 + }, + "services/webmail/src/lib/formatBytes.ts": { + "description": "A byte count as a person reads it.", + "tokens": 398, + "hash": "2e7b6fa34388eb04", + "size": 1390, + "mtimeMs": 1787579724171.29, + "updatedAt": "2026-10-06T11:06:20.905Z", + "source": "scan", + "importance": 0.0282 + }, + "services/webmail/src/lib/initials.ts": { + "description": "The two letters an avatar shows.", + "tokens": 236, + "hash": "b164548186ccd6fe", + "size": 826, + "mtimeMs": 1787094346494.656, + "updatedAt": "2026-10-06T11:06:20.905Z", + "source": "scan", + "importance": 0.051 + }, + "services/webmail/src/lib/mailCounts.ts": { + "description": "The arithmetic behind every number the mail screens display.", + "tokens": 2786, + "hash": "48af3fdf0cf46980", + "size": 9751, + "mtimeMs": 1787423393559.9287, + "updatedAt": "2026-10-06T11:06:20.905Z", + "source": "scan", + "symbols": [ + { + "name": "FolderSummary", + "kind": "section", + "startLine": 12, + "endLine": 17, + "tokens": 35 + }, + { + "name": "MessageSummary", + "kind": "section", + "startLine": 20, + "endLine": 24, + "tokens": 28 + }, + { + "name": "FolderMetrics", + "kind": "section", + "startLine": 26, + "endLine": 29, + "tokens": 20 + }, + { + "name": "ListFilter", + "kind": "section", + "startLine": 31, + "endLine": 31, + "tokens": 16 + }, + { + "name": "counter", + "kind": "fn", + "startLine": 34, + "endLine": 37, + "tokens": 49 + }, + { + "name": "folderMetrics", + "kind": "fn", + "startLine": 47, + "endLine": 57, + "tokens": 121 + }, + { + "name": "badgeCount", + "kind": "fn", + "startLine": 66, + "endLine": 69, + "tokens": 64 + }, + { + "name": "messageCounts", + "kind": "fn", + "startLine": 72, + "endLine": 82, + "tokens": 85 + }, + { + "name": "filterMessages", + "kind": "fn", + "startLine": 84, + "endLine": 88, + "tokens": 77 + }, + { + "name": "listHeaderCount", + "kind": "fn", + "startLine": 102, + "endLine": 112, + "tokens": 102 + }, + { + "name": "applySeen", + "kind": "fn", + "startLine": 120, + "endLine": 122, + "tokens": 49 + }, + { + "name": "FolderBadge", + "kind": "section", + "startLine": 125, + "endLine": 140, + "tokens": 174 + }, + { + "name": "folderBadge", + "kind": "fn", + "startLine": 151, + "endLine": 158, + "tokens": 85 + }, + { + "name": "ReaderActions", + "kind": "section", + "startLine": 161, + "endLine": 170, + "tokens": 85 + }, + { + "name": "readerActions", + "kind": "fn", + "startLine": 181, + "endLine": 196, + "tokens": 150 + }, + { + "name": "moveTargets", + "kind": "fn", + "startLine": 210, + "endLine": 230, + "tokens": 283 + }, + { + "name": "customFolders", + "kind": "fn", + "startLine": 245, + "endLine": 253, + "tokens": 111 + } + ], + "symbolSource": "ts", + "skeleton": "export interface FolderSummary {\nexport interface MessageSummary {\nexport interface FolderMetrics {\nexport type ListFilter = \"unread\" | \"attachment\" | null;\nfunction counter(value: unknown): number {\nexport function folderMetrics(\nexport function badgeCount(folders: FolderSummary[] | undefined | null, route: string): number {\nexport function messageCounts(messages: MessageSummary[]): {\nexport function filterMessages(messages: T[], filter: ListFilter): T[] {\nexport function listHeaderCount(input: {\nexport function applySeen(messages: T[], uid: number, seen: boolean): T[] {\nexport interface FolderBadge {\nexport function folderBadge(\nexport interface ReaderActions {\nexport function readerActions(folder: string): ReaderActions {\nexport function moveTargets(\nexport function customFolders(folders: FolderSummary[] | undefined | null): FolderSummary[] {", + "importance": 0.0402 + }, + "services/webmail/src/lib/paging.test.ts": { + "description": "Declares w", + "tokens": 464, + "hash": "adc926b9ef4c7ed6", + "size": 1622, + "mtimeMs": 1787574020278.871, + "updatedAt": "2026-10-06T11:06:20.905Z", + "source": "scan", + "imports": [ + "services/webmail/src/lib/paging.ts" + ], + "importance": 0.0144 + }, + "services/webmail/src/lib/paging.ts": { + "description": "The page numbers a pager should offer.", + "tokens": 273, + "hash": "926c3a3e4f900e8b", + "size": 954, + "mtimeMs": 1787574020276.871, + "updatedAt": "2026-10-06T11:06:20.905Z", + "source": "scan", + "importance": 0.0319 + }, + "services/webmail/src/lib/remoteImages.ts": { + "description": "Which senders may load their remote images.", + "tokens": 863, + "hash": "9279a6a265e460ad", + "size": 3018, + "mtimeMs": 1787414674392.4834, + "updatedAt": "2026-10-06T11:06:20.906Z", + "source": "scan", + "symbols": [ + { + "name": "senderKey", + "kind": "fn", + "startLine": 30, + "endLine": 36, + "tokens": 76 + }, + { + "name": "readTrusted", + "kind": "fn", + "startLine": 46, + "endLine": 55, + "tokens": 82 + }, + { + "name": "writeTrusted", + "kind": "fn", + "startLine": 57, + "endLine": 69, + "tokens": 131 + }, + { + "name": "isSenderTrusted", + "kind": "fn", + "startLine": 71, + "endLine": 75, + "tokens": 44 + }, + { + "name": "trustSender", + "kind": "fn", + "startLine": 77, + "endLine": 82, + "tokens": 58 + }, + { + "name": "revokeSender", + "kind": "fn", + "startLine": 84, + "endLine": 88, + "tokens": 48 + } + ], + "symbolSource": "ts", + "importance": 0.0213 + }, + "services/webmail/src/lib/roleLabel.ts": { + "description": "A role as a person reads it.", + "tokens": 200, + "hash": "c4e633ffb520dbc6", + "size": 697, + "mtimeMs": 1787579835488.5488, + "updatedAt": "2026-10-06T11:06:20.906Z", + "source": "scan", + "importance": 0.0159 + }, + "services/webmail/src/lib/rules.ts": { + "description": "Rules as a person describes them, and the Sieve the server runs.", + "tokens": 1672, + "hash": "4b255a815928466e", + "size": 5851, + "mtimeMs": 1787416727573.8374, + "updatedAt": "2026-10-06T11:06:20.906Z", + "source": "scan", + "symbols": [ + { + "name": "RuleField", + "kind": "section", + "startLine": 15, + "endLine": 15, + "tokens": 17 + }, + { + "name": "RuleCondition", + "kind": "section", + "startLine": 16, + "endLine": 16, + "tokens": 26 + }, + { + "name": "RuleAction", + "kind": "section", + "startLine": 17, + "endLine": 17, + "tokens": 19 + }, + { + "name": "Rule", + "kind": "section", + "startLine": 19, + "endLine": 27, + "tokens": 58 + }, + { + "name": "quote", + "kind": "fn", + "startLine": 43, + "endLine": 45, + "tokens": 31 + }, + { + "name": "escapeWildcards", + "kind": "fn", + "startLine": 54, + "endLine": 56, + "tokens": 28 + }, + { + "name": "testFor", + "kind": "fn", + "startLine": 59, + "endLine": 74, + "tokens": 169 + }, + { + "name": "actionFor", + "kind": "fn", + "startLine": 76, + "endLine": 87, + "tokens": 86 + }, + { + "name": "buildSieve", + "kind": "fn", + "startLine": 90, + "endLine": 95, + "tokens": 57 + }, + { + "name": "unquote", + "kind": "fn", + "startLine": 103, + "endLine": 105, + "tokens": 25 + }, + { + "name": "unescapeWildcards", + "kind": "fn", + "startLine": 107, + "endLine": 109, + "tokens": 28 + }, + { + "name": "parseSieve", + "kind": "fn", + "startLine": 120, + "endLine": 175, + "tokens": 494 + } + ], + "symbolSource": "ts", + "importance": 0.0278 + }, + "services/webmail/src/lib/sanitizer.ts": { + "description": "Sanitizes message HTML with DOMPurify (PLAN.md section 14.2).", + "tokens": 1142, + "hash": "facf598be2ff01e5", + "size": 3995, + "mtimeMs": 1787075616727.1992, + "updatedAt": "2026-10-06T11:06:20.906Z", + "source": "scan", + "symbols": [ + { + "name": "installDataUriHook", + "kind": "fn", + "startLine": 26, + "endLine": 34, + "tokens": 84 + }, + { + "name": "sanitizeEmailHtml", + "kind": "fn", + "startLine": 36, + "endLine": 72, + "tokens": 551 + }, + { + "name": "blockRemoteImages", + "kind": "fn", + "startLine": 78, + "endLine": 84, + "tokens": 91 + }, + { + "name": "unblockRemoteImages", + "kind": "fn", + "startLine": 87, + "endLine": 90, + "tokens": 52 + } + ], + "symbolSource": "ts", + "importance": 0.0396 + }, + "services/webmail/src/lib/signature.ts": { + "description": "The signature, on its way from the settings screen into a message.", + "tokens": 1107, + "hash": "a2f8a003b4a54829", + "size": 3872, + "mtimeMs": 1787414837859.6646, + "updatedAt": "2026-10-06T11:06:20.906Z", + "source": "scan", + "symbols": [ + { + "name": "installHook", + "kind": "fn", + "startLine": 31, + "endLine": 41, + "tokens": 114 + }, + { + "name": "sanitizeSignature", + "kind": "fn", + "startLine": 49, + "endLine": 60, + "tokens": 124 + }, + { + "name": "looksLikeHtml", + "kind": "fn", + "startLine": 75, + "endLine": 77, + "tokens": 24 + }, + { + "name": "escapeText", + "kind": "fn", + "startLine": 79, + "endLine": 85, + "tokens": 50 + }, + { + "name": "signatureToHtml", + "kind": "fn", + "startLine": 95, + "endLine": 104, + "tokens": 154 + } + ], + "symbolSource": "ts", + "importance": 0.0319 + }, + "services/webmail/src/lib/surfaceGate.ts": { + "description": "Which session a page needs before it can usefully render.", + "tokens": 445, + "hash": "ba304c1ef7f92c4e", + "size": 1557, + "mtimeMs": 1787075616727.2866, + "updatedAt": "2026-10-06T11:06:20.906Z", + "source": "scan", + "importance": 0.0389 + }, + "services/webmail/src/lib/useAccount.ts": { + "description": "Roles that may open the admin console.", + "tokens": 429, + "hash": "9dd0e30037630f6e", + "size": 1499, + "mtimeMs": 1785708839137.1448, + "updatedAt": "2026-10-06T11:06:20.906Z", + "source": "scan", + "importance": 0.0237 + }, + "services/webmail/src/lib/useDebounced.ts": { + "description": "Holds a value still until typing stops.", + "tokens": 170, + "hash": "eb2790e79c509405", + "size": 595, + "mtimeMs": 1787574075079, + "updatedAt": "2026-10-06T11:06:20.906Z", + "source": "scan", + "importance": 0.0215 + }, + "services/webmail/src/lib/useFolders.ts": { + "description": "The folder list, kept current.", + "tokens": 635, + "hash": "509901b101957e5d", + "size": 2221, + "mtimeMs": 1787075616727.374, + "updatedAt": "2026-10-06T11:06:20.906Z", + "source": "scan", + "symbols": [ + { + "name": "notifyMailStateChanged", + "kind": "fn", + "startLine": 27, + "endLine": 30, + "tokens": 43 + }, + { + "name": "useFolders", + "kind": "fn", + "startLine": 32, + "endLine": 58, + "tokens": 297 + } + ], + "symbolSource": "ts", + "imports": [ + "services/webmail/src/lib/useMailEvents.ts", + "services/webmail/src/lib/mailCounts.ts" + ], + "importance": 0.0244 + }, + "services/webmail/src/lib/useMailEvents.ts": { + "description": "Subscribes to the server's event stream and calls onEvent for each push.", + "tokens": 641, + "hash": "897ea447f80aba41", + "size": 2241, + "mtimeMs": 1785689161522.6934, + "updatedAt": "2026-10-06T11:06:20.906Z", + "source": "scan", + "symbols": [ + { + "name": "MailEvent", + "kind": "section", + "startLine": 5, + "endLine": 9, + "tokens": 28 + }, + { + "name": "useMailEvents", + "kind": "fn", + "startLine": 24, + "endLine": 73, + "tokens": 422 + } + ], + "symbolSource": "ts", + "importance": 0.0316 + }, + "services/webmail/src/mail-state-integrity.test.ts": { + "description": "The wiring behind the counters, the reader pane and the two surfaces.", + "tokens": 2149, + "hash": "29e6979c3381057b", + "size": 7520, + "mtimeMs": 1787423053296.819, + "updatedAt": "2026-10-06T11:06:20.906Z", + "source": "scan", + "symbols": [ + { + "name": "read", + "kind": "fn", + "startLine": 14, + "endLine": 14, + "tokens": 24 + }, + { + "name": "code", + "kind": "fn", + "startLine": 16, + "endLine": 20, + "tokens": 46 + } + ], + "symbolSource": "ts", + "skeleton": "const read = (path: string) => readFileSync(resolve(process.cwd(), path), \"utf8\");\nconst code = (path: string) =>", + "importance": 0.0144 + }, + "services/webmail/src/mailCounts.test.ts": { + "description": "The counters the interface shows.", + "tokens": 4397, + "hash": "b48902ef48d3cda4", + "size": 15389, + "mtimeMs": 1787422990631.982, + "updatedAt": "2026-10-06T11:06:20.906Z", + "source": "scan", + "importance": 0.0144 + }, + "services/webmail/src/middleware.ts": { + "description": "Gate for the two web surfaces.", + "tokens": 862, + "hash": "67ba2fdabb1f3f36", + "size": 3014, + "mtimeMs": 1787075616727.7083, + "updatedAt": "2026-10-06T11:06:20.906Z", + "source": "scan", + "symbols": [ + { + "name": "JwtClaims", + "kind": "section", + "startLine": 22, + "endLine": 27, + "tokens": 28 + }, + { + "name": "readClaims", + "kind": "fn", + "startLine": 30, + "endLine": 41, + "tokens": 119 + }, + { + "name": "isUsable", + "kind": "fn", + "startLine": 43, + "endLine": 51, + "tokens": 121 + }, + { + "name": "middleware", + "kind": "fn", + "startLine": 53, + "endLine": 79, + "tokens": 293 + } + ], + "symbolSource": "ts", + "imports": [ + "services/webmail/src/lib/surfaceGate.ts" + ], + "importance": 0.0144 + }, + "services/webmail/src/remoteImages.test.ts": { + "description": "Whether a message's remote images load.", + "tokens": 1007, + "hash": "32dc7f4130b49f29", + "size": 3522, + "mtimeMs": 1787414648587.2393, + "updatedAt": "2026-10-06T11:06:20.906Z", + "source": "scan", + "importance": 0.0144 + }, + "services/webmail/src/rules.test.ts": { + "description": "Rules, as a person describes them, turned into Sieve and back.", + "tokens": 2014, + "hash": "242edcc9030f1a4f", + "size": 7049, + "mtimeMs": 1787417860483.5532, + "updatedAt": "2026-10-06T11:06:20.906Z", + "source": "scan", + "symbols": [ + { + "name": "rule", + "kind": "fn", + "startLine": 13, + "endLine": 21, + "tokens": 52 + } + ], + "symbolSource": "ts", + "skeleton": "const rule = (over: Partial = {}): Rule => ({", + "imports": [ + "services/webmail/src/lib/rules.ts" + ], + "importance": 0.0144 + }, + "services/webmail/src/sanitizer.test.ts": { + "description": "These run under jsdom. Under the default node environment DOMPurify was", + "tokens": 1002, + "hash": "0026995aaae81e19", + "size": 3505, + "mtimeMs": 1785689161522.8435, + "updatedAt": "2026-10-06T11:06:20.906Z", + "source": "scan", + "imports": [ + "services/webmail/src/lib/sanitizer.ts" + ], + "importance": 0.0144 + }, + "services/webmail/src/sidebarBadge.test.ts": { + "description": "What the rail actually puts on screen, folder by folder.", + "tokens": 639, + "hash": "ad3c3c6c385c0492", + "size": 2236, + "mtimeMs": 1787423371226.8423, + "updatedAt": "2026-10-06T11:06:20.906Z", + "source": "scan", + "symbols": [ + { + "name": "rendered", + "kind": "fn", + "startLine": 12, + "endLine": 15, + "tokens": 49 + } + ], + "symbolSource": "ts", + "imports": [ + "services/webmail/src/lib/mailCounts.ts" + ], + "importance": 0.0144 + }, + "services/webmail/src/signature.test.ts": { + "description": "The signature is edited in one place and injected into the composer in", + "tokens": 1022, + "hash": "ffa0a382d4f5af50", + "size": 3575, + "mtimeMs": 1787414756517.2598, + "updatedAt": "2026-10-06T11:06:20.907Z", + "source": "scan", + "imports": [ + "services/webmail/src/lib/signature.ts" + ], + "importance": 0.0144 + }, + "services/webmail/src/surfaceGate.test.ts": { + "description": "Which session, if any, a page needs before it may render.", + "tokens": 606, + "hash": "f7e336496f2f9958", + "size": 2120, + "mtimeMs": 1787075616727.8108, + "updatedAt": "2026-10-06T11:06:20.907Z", + "source": "scan", + "imports": [ + "services/webmail/src/lib/surfaceGate.ts" + ], + "importance": 0.0144 + }, + "services/webmail/src/wellknown-routing.test.ts": { + "description": "The well-known documents senders fetch over HTTPS.", + "tokens": 729, + "hash": "024e267b24eeabcd", + "size": 2551, + "mtimeMs": 1788831604906.3716, + "updatedAt": "2026-10-06T11:06:20.907Z", + "source": "scan", + "importance": 0.0144 + }, + "services/webmail/tailwind.config.js": { + "description": "Tailwind CSS configuration", + "tokens": 1064, + "hash": "6632a7f0a61f4274", + "size": 3724, + "mtimeMs": 1787094218189.5737, + "updatedAt": "2026-10-06T11:06:20.907Z", + "source": "scan", + "importance": 0.0144 + }, + "services/webmail/tsconfig.json": { + "description": "TypeScript configuration", + "tokens": 174, + "hash": "5ff4001d8b128359", + "size": 607, + "mtimeMs": 1785635482530.658, + "updatedAt": "2026-10-06T11:06:20.907Z", + "source": "scan", + "importance": 0.0144 + }, + "services/webmail/vitest.config.ts": { + "description": "Vitest test configuration", + "tokens": 93, + "hash": "69a6703ffbb48a12", + "size": 324, + "mtimeMs": 1785689161522.8938, + "updatedAt": "2026-10-06T11:06:20.907Z", + "source": "scan", + "importance": 0.0144 + }, + "services/webmail/src/components/MessageReader.tsx": { + "description": "The document the body frame renders, or null for a text-only message.", + "tokens": 4736, + "hash": "3f89e8a69e4f4e41", + "size": 16575, + "mtimeMs": 1791895211654.4243, + "updatedAt": "2026-10-13T12:40:11.730Z", + "source": "hook", + "symbols": [ + { + "name": "ReaderMessage", + "kind": "section", + "startLine": 22, + "endLine": 32, + "tokens": 50 + }, + { + "name": "ReaderRow", + "kind": "section", + "startLine": 33, + "endLine": 39, + "tokens": 36 + }, + { + "name": "ReaderActions", + "kind": "section", + "startLine": 40, + "endLine": 47, + "tokens": 45 + }, + { + "name": "MoveTarget", + "kind": "section", + "startLine": 48, + "endLine": 52, + "tokens": 17 + }, + { + "name": "Props", + "kind": "section", + "startLine": 53, + "endLine": 74, + "tokens": 196 + }, + { + "name": "splitSender", + "kind": "fn", + "startLine": 75, + "endLine": 81, + "tokens": 98 + }, + { + "name": "initialsOf", + "kind": "fn", + "startLine": 82, + "endLine": 88, + "tokens": 99 + }, + { + "name": "attachmentKind", + "kind": "fn", + "startLine": 89, + "endLine": 106, + "tokens": 402 + }, + { + "name": "MessageReader", + "kind": "fn", + "startLine": 107, + "endLine": 383, + "tokens": 3687 + } + ] + }, + "services/webmail/src/components/RecipientInput.tsx": { + "description": "Entries as they will be sent: `Name ` or a bare address.", + "tokens": 1486, + "hash": "3a0aed48a083f576", + "size": 5198, + "mtimeMs": 1791895865256.09, + "updatedAt": "2026-10-13T12:51:05.360Z", + "source": "hook", + "symbols": [ + { + "name": "Props", + "kind": "section", + "startLine": 8, + "endLine": 27, + "tokens": 220 + }, + { + "name": "RecipientInput", + "kind": "fn", + "startLine": 28, + "endLine": 146, + "tokens": 1196 + } + ] + }, + "services/auth/src/accounts.ts": { + "description": "Several webmail accounts signed in at once, and a session that stays alive", + "tokens": 715, + "hash": "56859b67617d6304", + "size": 2500, + "mtimeMs": 1792690877532.1526, + "updatedAt": "2026-10-22T17:41:17.634Z", + "source": "hook", + "symbols": [ + { + "name": "accountCookieName", + "kind": "fn", + "startLine": 26, + "endLine": 29, + "tokens": 33 + }, + { + "name": "parseCookies", + "kind": "fn", + "startLine": 30, + "endLine": 42, + "tokens": 152 + }, + { + "name": "accountTokens", + "kind": "fn", + "startLine": 43, + "endLine": 53, + "tokens": 144 + }, + { + "name": "shouldRenew", + "kind": "fn", + "startLine": 54, + "endLine": 57, + "tokens": 36 + } + ] + } + }, + "preamble": [ + "> Project structure index. Auto-maintained by OpenWolf hooks and daemon.", + "> Run `openwolf scan` to generate, or wait for the first Claude Code session.", + "> Status: Pending initial scan" + ] +} \ No newline at end of file diff --git a/.wolf/anatomy.md b/.wolf/anatomy.md new file mode 100644 index 0000000..30de6cb --- /dev/null +++ b/.wolf/anatomy.md @@ -0,0 +1,648 @@ +# anatomy.md + +> Auto-maintained by OpenWolf. Last scanned: 2026-10-22T17:41:17.634Z +> Files: 439 tracked | Anatomy hits: 0 | Misses: 0 + +> Project structure index. Auto-maintained by OpenWolf hooks and daemon. +> Run `openwolf scan` to generate, or wait for the first Claude Code session. +> Status: Pending initial scan + +## ./ + +- `.gitignore` β€” Git ignore rules (~277 tok) +- `.trivyignore` β€” Findings that have been assessed and deliberately accepted. (~211 tok) +- `CLAUDE.md` β€” OpenWolf (~99 tok) +- `docker-compose.build.yaml` β€” Development overlay: builds the images from source instead of pulling them. (~260 tok) +- `docker-compose.yaml` β€” Docker Compose services (~5222 tok) +- `LICENSE` β€” Project license (~287 tok) +- `Makefile` β€” Make build targets (~1327 tok) +- `README.md` β€” Project documentation (~3456 tok) + +## .github/workflows/ + +- `ci.yml` β€” CI: CI (~2438 tok) +- `release.yml` β€” CI: Release (~1863 tok) + +## config/rspamd/ + +- `actions.conf` β€” Score thresholds from PLAN.md section 10.1. The principle behind them is (~108 tok) +- `logging.inc` (~10 tok) +- `options.inc` β€” DNS resolution is needed for RBL, SPF and DKIM lookups. (~33 tok) +- `ratelimit.conf` β€” Rate limiting of PLAN.md section 5.2. (~408 tok) +- `worker-normal.inc` β€” The protocols service reaches Rspamd over the internal network only; the (~40 tok) + +## docs/ + +- `DEPLOYMENT.md` β€” Deploying LambdaMail on Coolify (~2605 tok) +- `OPERATIONS.md` β€” Operating LambdaMail (~2229 tok) + +## migrations/ + +- `0001_init_schema.down.sql` β€” 0001_init_schema.down.sql (~225 tok) +- `0001_init_schema.up.sql` β€” 0001_init_schema.up.sql (~5730 tok) +- `0002_add_is_system_to_aliases.down.sql` β€” Revert is_system column from aliases table (~29 tok) +- `0002_add_is_system_to_aliases.up.sql` β€” Add is_system column to aliases table to track mandatory system security aliases (~45 tok) +- `0003_create_report_tables.down.sql` (~47 tok) +- `0003_create_report_tables.up.sql` β€” 0003_create_report_tables.up.sql (~575 tok) +- `0004_index_outbound_stale_locks.down.sql` (~14 tok) +- `0004_index_outbound_stale_locks.up.sql` β€” Delivery workers claim a job by moving it to SENDING with a lock deadline. (~105 tok) +- `0005_create_acme_tables.down.sql` (~34 tok) +- `0005_create_acme_tables.up.sql` β€” Self-managed ACME state (PLAN.md section 8.3, mode B). (~752 tok) +- `0006_add_user_settings_and_system_config.down.sql` β€” SQL: 2 alter(s) (~44 tok) +- `0006_add_user_settings_and_system_config.up.sql` β€” SQL: tables: system_config, 2 alter(s) (~135 tok) +- `0007_reconcile_folder_counters.down.sql` β€” Irreversible by nature: this migration replaced incorrect counter values and (~90 tok) +- `0007_reconcile_folder_counters.up.sql` β€” Reconcile folders.unread_count and folders.total_count with the messages (~819 tok) +- `0008_add_reports_folder.down.sql` β€” Only removes the folder where it is still empty. A Reports folder holding (~94 tok) +- `0008_add_reports_folder.up.sql` β€” Give every existing mailbox a Reports folder. (~185 tok) +- `0009_report_ingestion_idempotency.down.sql` (~30 tok) +- `0009_report_ingestion_idempotency.up.sql` β€” Make storing a report idempotent, keyed on the reporter and their own id (~425 tok) +- `0010_vacation_reply_log.down.sql` (~12 tok) +- `0010_vacation_reply_log.up.sql` β€” Who has already been told the mailbox is unattended. (~257 tok) +- `0011_backfill_markers.down.sql` (~10 tok) +- `0011_backfill_markers.up.sql` β€” Records one-shot data repairs so they run once rather than on every start. (~176 tok) +- `0012_mailbox_avatars.down.sql` (~11 tok) +- `0012_mailbox_avatars.up.sql` β€” The profile photo a mailbox shows inside LambdaMail. (~256 tok) +- `0013_domain_bimi.down.sql` (~10 tok) +- `0013_domain_bimi.up.sql` β€” The BIMI logo a domain publishes, and the certificate that vouches for it. (~188 tok) + +## scripts/ + +- `dev-seed.sql` β€” Dev-only fixture data for local SMTP testing. Not a schema migration - (~533 tok) +- `lang-lint.mjs` β€” Fails the build if tracked source contains non-ASCII characters or common (~483 tok) + +## services/auth/ + +- `Dockerfile` β€” Docker container definition (~408 tok) +- `package.json` β€” Node.js package manifest (~138 tok) +- `tsconfig.build.json` β€” TypeScript build configuration (~83 tok) +- `tsconfig.json` β€” TypeScript configuration (~88 tok) +- `vitest.config.ts` β€” Vitest test configuration (~153 tok) + +## services/auth/src/ + +- `accounts.ts` β€” Several webmail accounts signed in at once, and a session that stays alive (~715 tok) +- `auth.test.ts` β€” Declares password (~3292 tok) +- `cli.ts` β€” Operator commands for a fresh deployment. (~2135 tok) +- `crypto.ts` β€” Derives the key-encryption key. Unlike the previous implementation there is (~1465 tok) +- `db.ts` β€” Lazily opens the shared connection pool. It is lazy so that unit tests which (~266 tok) +- `diagnostics.test.ts` β€” Declares UserFacts (~1317 tok) +- `diagnostics.ts` β€” Checks an operator can run by hand, against the whole server or one user. (~1557 tok) +- `health.test.ts` β€” Declares withServer (~470 tok) +- `health.ts` β€” Builds the /health/live and /health/ready handler shared by the auth and (~215 tok) +- `imageValidation.test.ts` β€” Declares png (~741 tok) +- `imageValidation.ts` β€” What an uploaded avatar is allowed to be. (~598 tok) +- `mfaHelpers.ts` β€” Generates single-use recovery codes. Only the hashes are ever stored, so a (~494 tok) +- `pagination.test.ts` β€” Declares at (~661 tok) +- `pagination.ts` β€” Page and filter parameters, read from a URL and made safe for SQL. (~510 tok) +- `repository.ts` β€” Decides whether this account must present a second factor now. (~14732 tok) +- `router.test.ts` β€” Enrolls a real second factor and returns the shared secret. (~16096 tok) +- `router.ts` β€” Cookie scoped to the whole origin: the API lives under /api, so a cookie (~15221 tok) +- `server.ts` β€” Declares port (~236 tok) +- `session.ts` β€” The signing secret. There is deliberately no default. (~1406 tok) +- `surfaceAccess.test.ts` β€” Which surface a login opens, and what it costs to cross between them. (~1640 tok) +- `surfaceAccess.ts` β€” Which surface a login opens, and what crossing between them costs. (~712 tok) +- `totp.ts` β€” Renders the otpauth:// URI an authenticator app scans. (~1037 tok) + +## services/migrations/ + +- `Dockerfile` β€” Docker container definition (~241 tok) + +## services/protocols/ + +- `.go-arch-lint.yml` β€” health: (~99 tok) +- `Dockerfile` β€” Docker container definition (~312 tok) +- `go.mod` β€” Go module definition (~326 tok) +- `go.sum` β€” Go dependency checksums (~2706 tok) + +## services/protocols/cmd/lambdamail-protocols/ + +- `acme_mode.go` β€” tlsaPublisher (26 fields); methods: PublishTlsa (~1204 tok) +- `backfill_attachments.go` β€” Declares attachmentBackfillName (~717 tok) +- `backfill_reports.go` (~501 tok) +- `bimi_store.go` β€” bimiStore (4 fields); methods: Save, Load, Delete (~256 tok) +- `config.go` β€” config (52 fields) (~1251 tok) +- `disk_other.go` β€” go:build !unix (~98 tok) +- `disk_unix.go` β€” go:build unix (~183 tok) +- `dns_reconciler.go` β€” domainReconciler (14 fields); methods: ReconcileDomain (~961 tok) +- `main.go` β€” Command lambdamail-protocols is the entry point for the protocols service. (~8598 tok) +- `preflight.go` (~994 tok) + +## services/protocols/internal/application/port/ + +- `arc_sealer.go` β€” ArcSealer adds an Authenticated Received Chain set to a message (~222 tok) +- `auth_repository.go` β€” Interface: AuthRepository (2 methods) (~205 tok) +- `blob_reader.go` β€” Interface: BlobReader (2 methods) (~65 tok) +- `blob_storage.go` β€” Interface: BlobStorage (2 methods) (~152 tok) +- `cert_provider.go` β€” CertProvider supplies the TLS certificate for STARTTLS. Its signature (~104 tok) +- `certificate_store.go` β€” Interface: CertificateStore (8 methods) (~524 tok) +- `content_scanner.go` β€” Interface: ContentScanner (2 methods) (~93 tok) +- `copy_repository.go` β€” CopiedMessage records one message's UID before and after a COPY/MOVE. (~244 tok) +- `dkim_repository.go` β€” DkimSigningKey is one active signing key, already decrypted and ready to (~304 tok) +- `dns_provider.go` β€” Interface: DnsProvider (6 methods) (~168 tok) +- `dns_verifier.go` β€” Interface: DnsVerifier (2 methods) (~190 tok) +- `expunge_repository.go` β€” ExpungeRepository permanently marks messages as expunged (soft delete - (~208 tok) +- `flag_repository.go` β€” FlagOp mirrors imap.StoreFlagsOp without importing the imap package here (~222 tok) +- `imap_folder_repository.go` β€” ImapFolderRecord is the IMAP-relevant subset of a folder row. (~185 tok) +- `inbound_message_repository.go` β€” Interface: InboundMessageRepository (2 methods) (~626 tok) +- `mail_authenticator.go` β€” Interface: MailAuthenticator (2 methods) (~424 tok) +- `mailbox_repository.go` β€” declares the interfaces the application layer depends on. (~386 tok) +- `message_query_repository.go` β€” Interface: MessageQueryRepository (2 methods) (~211 tok) +- `mx_resolver.go` β€” Interface: MXResolver (2 methods) (~38 tok) +- `outbound_repository.go` β€” Interface: OutboundRepository (5 methods) (~200 tok) +- `outbox.go` β€” Interface: OutboxRepository (4 methods) (~237 tok) +- `report_repository.go` β€” Interface: ReportRepository (8 methods) (~617 tok) +- `sieve_repository.go` β€” SieveScriptRecord represents a persistent Sieve script row in PostgreSQL. (~238 tok) +- `system_alias_repository.go` β€” SystemAliasRepository manages mandatory system security aliases (postmaster, abuse, dmarc, tlsrpt). (~75 tok) +- `tls_policy_resolver.go` β€” Interface: TLSPolicyResolver (2 methods) (~203 tok) +- `webmail_repository.go` β€” Interface: WebmailRepository (13 methods) (~1306 tok) + +## services/protocols/internal/application/usecase/ + +- `apply_rules_test.go` β€” stubSieveScripts (35 fields); methods: ActiveScriptFor (~2504 tok) +- `apply_rules.go` β€” ApplyRulesUseCase (20 fields); methods: For (~990 tok) +- `backfill_reports_test.go` β€” stubDeliveredReportStore (35 fields); methods: ListUningestedReports, MoveToReportsFolder, ReadByID (~1565 tok) +- `backfill_reports.go` β€” BackfillReportsUseCase (16 fields); methods: Run (~846 tok) +- `imap_session_test.go` β€” fakeAuthRepository (40 fields); methods: FindByAddress, ListFolders, FindByName, Expunge (~1828 tok) +- `imap_session.go` β€” ImapSessionUseCase (25 fields); methods: SetTrackerManager, GetTrackerManager, Login, ListFolders (~1085 tok) +- `inbound_auth_test.go` β€” stubAuthenticator (19 fields); methods: Authenticate (~1438 tok) +- `inbound_scan_headers_test.go` β€” capturingBlobStorage (9 fields); methods: Store, Scan (~882 tok) +- `inbound_system_message_test.go` β€” junkingScanner (7 fields); methods: Scan (~1144 tok) +- `ingest_delivered_reports_test.go` β€” recordingReportRepo (48 fields); methods: SaveDmarcReport, SaveTlsRptReport (~3334 tok) +- `ingest_delivered_reports.go` β€” IngestDeliveredReportsUseCase (34 fields); methods: Ingest (~1905 tok) +- `ingest_reports_test.go` β€” fakeReportRepository (8 fields); methods: SaveDmarcReport, SaveTlsRptReport (~612 tok) +- `ingest_reports.go` β€” IngestReportsUseCase (15 fields); methods: IngestDmarc, IngestTlsRpt (~347 tok) +- `mailbox_tracker_manager.go` β€” MailboxTrackerManager (9 fields); methods: GetTracker, NotifyNumMessages, NotifyMessageFlags, NotifyExpunge (~585 tok) +- `manage_folders_test.go` β€” stubFolderStore (47 fields); methods: ListFolders, CreateFolder, RenameFolder, DeleteFolder (~2128 tok) +- `manage_folders.go` β€” ManageFoldersUseCase (52 fields); methods: Create, Rename, Delete (~1304 tok) +- `managesieve_session_test.go` β€” fakeSieveRepository (27 fields); methods: GetScript, PutScript, ListScripts, SetActiveScript (~1114 tok) +- `managesieve_session.go` β€” ManageSieveSessionUseCase (45 fields); methods: Login, PutScript, GetScript, ListScripts (~1011 tok) +- `message_headers_test.go` β€” TestExtractMessageHeaders_ReadsSubjectSenderAndSnippet, TestExtractMessageHeaders_DecodesEncodedWords, TestExtractMessageHeaders_FlagsMultipartMixe... (~1604 tok) +- `message_headers.go` β€” MessageHeaders (33 fields) (~1388 tok) +- `message_render_test.go` β€” TestRenderMessage_ExtractsHeadersAndText, TestRenderMessage_LeavesDateEmptyWhenHeaderAbsent, TestRenderMessage_PrefersPlainTextAndListsAttachments,... (~1976 tok) +- `message_render.go` β€” RenderedMessage (43 fields) (~2160 tok) +- `outbound_dsn_test.go` β€” TestOutboundWorker_PermanentSmtpFailureBouncesImmediately, TestOutboundWorker_TemporarySmtpFailureIsDeferred, TestOutboundWorker_DsnToFanOutSenderD... (~1480 tok) +- `outbound_local_delivery_test.go` β€” countingMXResolver (9 fields); methods: LookupMX (~1114 tok) +- `outbound_tls_policy_test.go` β€” stubPolicyResolver (29 fields); methods: Resolve (~1582 tok) +- `outbound_worker_test.go` β€” fakeOutboundRepo (18 fields); methods: Enqueue, FetchNextReady, CountRecipientsSince, UpdateJob (~744 tok) +- `outbound_worker.go` β€” OutboundWorkerUseCase (72 fields); methods: SetRelay, SetPolicyResolver, ProcessBatch (~4121 tok) +- `outbox_relay.go` β€” OutboxRelay (16 fields); methods: Run, PublishBatch (~551 tok) +- `pop3_session_test.go` β€” fakeMessageQueryRepository (12 fields); methods: ListMessages (~706 tok) +- `pop3_session.go` β€” Pop3SessionUseCase (31 fields); methods: Login, GetInbox, ReadBlob, ExpungeMessages (~690 tok) +- `preflight_test.go` β€” TestPreflight_HealthyDeploymentDoesNotBlock, TestPreflight_BlockedPort25IsCriticalAndSuggestsSmarthost, TestPreflight_SmarthostSkipsPort25Check, Te... (~2796 tok) +- `preflight.go` β€” CheckResult (112 fields); methods: Blocking, String, Execute (~3892 tok) +- `process_inbound_email_test.go` β€” fakeMailboxRepository (36 fields); methods: FindActiveByAddress, ResolveDeliveryTargets, FindActiveByAddress, ResolveDeliveryTargets (~2332 tok) +- `process_inbound_email.go` β€” contains the application's use cases (Clean Architecture layer 2). (~4084 tok) +- `process_outbound_email_test.go` β€” fakeAuthRepo (31 fields); methods: FindByAddress, CountRecipientsSince, Sign (~1768 tok) +- `process_outbound_email.go` β€” ProcessOutboundEmailInput (50 fields); methods: Authenticate, Submit (~1739 tok) +- `provision_dkim_keys_test.go` β€” fakeDkimStore (26 fields); methods: FindPublicKey, Insert, FindPublicKey, Insert (~1089 tok) +- `provision_dkim_keys.go` β€” Interface: DkimKeyProvisioner (5 methods) (~1121 tok) +- `relay_test.go` β€” fakeRelay (59 fields) (~3001 tok) +- `relay.go` β€” RelayConfig (42 fields); methods: Configured, Address, SpfInclude (~1588 tok) +- `rollover_tlsa_test.go` β€” TestRollover_StableWhenRenewalIsFarAway, TestRollover_StartsWhenRenewalApproaches, TestRollover_PublishesBothAssociationsWhileInFlight, TestRollove... (~2109 tok) +- `rollover_tlsa.go` β€” RolloverConfig (50 fields); methods: WithClock, Decide, ShouldRenewNow (~2470 tok) +- `scanning_pipeline_test.go` β€” mockScanner (9 fields); methods: Scan (~405 tok) +- `scanning_pipeline.go` β€” ScanningPipeline (13 fields); methods: Scan (~374 tok) +- `smtp_rejection.go` β€” SmtpRejection is a use case's decision to refuse a message, carrying the (~650 tok) +- `sync_dns_conflict_test.go` β€” stubDnsVerifier (32 fields); methods: VerifyRecord (~1548 tok) +- `sync_dns_records_test.go` β€” fakeDnsProvider (26 fields); methods: GetZoneID, ListRecords, CreateRecord, UpdateRecord (~1060 tok) +- `sync_dns_records.go` β€” SyncDnsRecordsInput (75 fields); methods: String, SetVerifier, Execute (~2481 tok) +- `vacation_reply_test.go` β€” TestReplyNamesTheMessageItAnswers, TestReplyCarriesTheConversationChain, TestReplyStartsTheChainWhenThereIsNone, TestReplyOmitsThreadingWhenThereIs... (~3030 tok) +- `vacation_reply.go` β€” pendingVacation (42 fields); methods: SetVacationSender, SetVacationSuppression (~2544 tok) +- `vacation_suppression_test.go` β€” stubVacationLog (18 fields); methods: LastRepliedAt, RecordReply (~1409 tok) +- `vacation_suppression.go` β€” VacationSuppression (19 fields); methods: ShouldReply, Record (~778 tok) +- `webmail_compose_test.go` β€” stubWebmailRepo (47 fields); methods: FindMailboxIDByAddress, ListFolders, ListMessages, GetMessageBlob (~4946 tok) +- `webmail_mail.go` β€” WebmailUseCase (81 fields); methods: WithLocalFiling, MailboxIDFor, ListFolders, ListMessages (~5136 tok) + +## services/protocols/internal/domain/bimi/ + +- `svg_test.go` β€” TestValidate_AcceptsAConformingLogo, TestValidate_RequiresTheProfileReceiversLookFor, TestValidate_RequiresASquareViewBox, TestValidate_RequiresExa... (~1630 tok) +- `svg.go` β€” validates the logo a domain publishes for BIMI. (~1575 tok) + +## services/protocols/internal/domain/entity/ + +- `dkim_key_pair_test.go` β€” TestNewDkimKeyPair_StartsPending, TestDkimKeyPair_Activate_SetsActive (~139 tok) +- `dkim_key_pair.go` β€” DkimStatus is the lifecycle state of a DkimKeyPair (PLAN.md section 9, dkim_keys.status). (~317 tok) +- `dmarc_report_test.go` β€” TestParseDmarcXmlReport_ValidXML, TestParseTlsRptReport_ValidJSON (~659 tok) +- `dmarc_report.go` β€” DmarcRecord (59 fields) (~1261 tok) +- `dmarc_zip_test.go` β€” TestDmarcReportParsesZippedPayload, TestDmarcReportStillParsesPlainXml, TestDmarcReportRejectsEmptyZip (~708 tok) +- `dns_record_idempotency_test.go` β€” TestBuildDnsRecordSpecs_MtaStsIdIsStableAcrossCalls, TestBuildDnsRecordSpecs_MtaStsIdChangesWithPolicy, TestDnsRecord_EqualsNormalized_DetectsPrior... (~740 tok) +- `dns_record_test.go` β€” TestBuildDnsRecordSpecs_GeneratesMandatoryRecords, TestDkimRecordsAreOmittedWhenThereIsNoKey, TestDkimRecordsArePublishedWhenKeysExist, TestOnlyThe... (~2424 tok) +- `dns_record.go` β€” DnsRecord (47 fields); methods: EqualsNormalized (~2951 tok) +- `domain_test.go` β€” TestNewDomain_StartsActiveWithNoMailboxes, TestDomain_Deactivate_SucceedsWithNoActiveMailboxes, TestDomain_Deactivate_FailsWithActiveMailboxes (~308 tok) +- `domain.go` β€” contains the domain's aggregate roots and entities (Clean Architecture layer 1). (~398 tok) +- `folder_test.go` β€” TestFolder_NextUID_StartsAtOneAndIsMonotonic (~141 tok) +- `folder.go` β€” Folder is an IMAP folder entity. uidNext is strictly monotonic (RFC 3501 section 2.3.1.1), (~168 tok) +- `mailbox_test.go` β€” TestNewMailbox_StartsWithZeroUsage, TestMailbox_RecordUsage_AllowsUpToQuota, TestMailbox_RecordUsage_RejectsOverQuota (~329 tok) +- `mailbox.go` β€” Mailbox (10 fields); methods: Address, UsedBytes, Quota, RecordUsage (~324 tok) +- `outbound_job_test.go` β€” TestCalculateNextBackoff_FollowsRetryTable, TestCalculateNextBackoff_ExpiredJobIsPermanent (~532 tok) +- `outbound_job.go` β€” OutboundJob (35 fields) (~705 tok) +- `sieve_script.go` β€” SieveScript represents a user's Sieve filtering script entity. (~68 tok) +- `sieve_validator_test.go` β€” TestValidateSieveScript_ValidScripts, TestValidateSieveScript_InvalidScripts (~396 tok) +- `sieve_validator.go` β€” ValidateSieveScript (~644 tok) +- `tls_policy_enforcement_test.go` β€” TestTLSPolicy_DaneTakesPrecedenceOverMtaSts, TestTLSPolicy_OpportunisticDoesNotRequireValidation, TestTLSPolicy_MtaStsTestingDoesNotEnforce, TestTL... (~1571 tok) +- `tls_policy_test.go` β€” TestTLSPolicy_Effective_DANETakesPrecedenceOverMTASTS, TestTLSPolicy_Effective_FallsBackToMTASTSWithoutDANE, TestTLSPolicy_Effective_FallsBackToOpp... (~232 tok) +- `tls_policy.go` β€” TLSPolicy (35 fields); methods: Effective, RequiresValidation, CoversHost, MatchesCertificate (~1537 tok) +- `tls_rpt_report_test.go` β€” TestTlsRptReportCarriesThePolicyDomain, TestTlsRptReportParsesGzippedPayload (~603 tok) +- `tls_rpt_report.go` β€” TlsRptPolicy (34 fields) (~718 tok) + +## services/protocols/internal/domain/event/ + +- `events_test.go` β€” TestEmailReceived_ImplementsDomainEvent, TestEmailBounced_ImplementsDomainEvent (~138 tok) +- `events.go` β€” contains the domain events listed in PLAN.md section 3. (~1066 tok) + +## services/protocols/internal/domain/sieve/ + +- `parser.go` β€” Interface: statement (12 methods) (~2127 tok) +- `sieve_test.go` β€” TestRuleFilesTheMessageInItsFolder, TestRuleThatDoesNotMatchLeavesTheMessageAlone, TestHeaderNameIsCaseInsensitive, TestContainsIsCaseInsensitiveOn... (~4735 tok) +- `sieve.go` β€” evaluates the mail-filtering rules a mailbox owner writes. (~926 tok) +- `test.go` β€” Interface: test (6 methods) (~1741 tok) +- `vacation.go` β€” shouldAutoReply decides whether a message may be answered automatically. (~701 tok) + +## services/protocols/internal/domain/valueobject/ + +- `autoconfig_xml_test.go` β€” TestBuildThunderbirdAutoconfigXML, TestBuildOutlookAutodiscoverXML (~220 tok) +- `autoconfig_xml.go` β€” BuildThunderbirdAutoconfigXML, BuildOutlookAutodiscoverXML (~735 tok) +- `delivery_status_code_test.go` β€” TestNewDeliveryStatusCode_AcceptsSuccess, TestNewDeliveryStatusCode_AcceptsPermanentFailure, TestNewDeliveryStatusCode_RejectsInvalidClass (~220 tok) +- `delivery_status_code.go` β€” DeliveryStatusCode (6 fields); methods: String (~206 tok) +- `domain_name_test.go` β€” TestNewDomainName_AcceptsValidFQDN, TestNewDomainName_RejectsEmpty, TestNewDomainName_RejectsLabelWithoutDot, TestNewDomainName_RejectsTrailingHyph... (~398 tok) +- `domain_name.go` β€” DomainName (13 fields); methods: String, IsPunycode (~403 tok) +- `dsn_bounce_test.go` β€” TestBuildDsnReport_PreventsBounceLoopForEmptySender, TestBuildDsnReport_GeneratesValidMIME, TestBuildDsnReport_UsesTheConfiguredMailHost, TestBuild... (~721 tok) +- `dsn_bounce.go` β€” BuildDsnReport (~1106 tok) +- `email_address_test.go` β€” TestNewEmailAddress_AcceptsValidAddress, TestNewEmailAddress_RejectsMissingAtSign, TestNewEmailAddress_RejectsEmptyLocalPart, TestNewEmailAddress_R... (~411 tok) +- `email_address.go` β€” contains the domain's immutable, self-validating value objects. (~430 tok) +- `message_id_test.go` β€” TestNewMessageID_AcceptsAngleBracketForm, TestNewMessageID_RejectsEmpty, TestNewMessageID_RejectsMissingAngleBrackets (~202 tok) +- `message_id.go` β€” MessageID (6 fields); methods: String (~186 tok) +- `mta_sts_policy_test.go` β€” TestMtaStsPolicy_Format (~178 tok) +- `mta_sts_policy.go` β€” MtaStsPolicy (14 fields); methods: Format (~260 tok) +- `password_hash_test.go` β€” TestNewPasswordHash_AcceptsArgon2idPHCString, TestNewPasswordHash_RejectsNonArgon2idScheme, TestNewPasswordHash_RejectsEmpty (~208 tok) +- `password_hash.go` β€” PasswordHash (5 fields); methods: String (~223 tok) +- `protocol_security_level_test.go` β€” TestProtocolSecurityLevel_StringRoundTrip (~120 tok) +- `protocol_security_level.go` β€” ProtocolSecurityLevel is the effective TLS requirement for a listener (see PLAN.md section 4). (~190 tok) +- `quota_limit_test.go` β€” TestNewQuotaLimit_AcceptsPositiveBytes, TestNewQuotaLimit_RejectsZero, TestNewQuotaLimit_RejectsNegative, TestQuotaLimit_Exceeds (~261 tok) +- `quota_limit.go` β€” QuotaLimit is a strictly positive byte quota (see PLAN.md section 3, Mailbox invariant). (~187 tok) +- `scan_result_test.go` β€” TestNewCleanScanResult, TestNewVirusScanResult (~130 tok) +- `scan_result.go` β€” ScanResult (16 fields) (~268 tok) +- `spam_verdict_test.go` β€” TestSpamVerdictFromScore_MatchesThresholds (~164 tok) +- `spam_verdict.go` β€” SpamVerdict classifies a message by Rspamd score (thresholds from PLAN.md section 10.1). (~136 tok) +- `tlsa_record_test.go` β€” TestNewTLSARecord_AcceptsUsage3Selector1MatchingType1, TestNewTLSARecord_RejectsUsage2, TestNewTLSARecord_RejectsWrongLengthHash (~328 tok) +- `tlsa_record.go` β€” TLSARecord models a DANE TLSA RR (RFC 6698/7671). Usage 2 (PKIX-CA) is rejected (~344 tok) + +## services/protocols/internal/e2e/ + +- `antispam_antivirus_e2e_test.go` β€” TestAntispamAntivirusEndToEnd (~1692 tok) +- `dkim_rotation_e2e_test.go` β€” TestDkimRotationLeavesAUsableKey (~1352 tok) +- `dns_sync_e2e_test.go` β€” e2eFakeDnsProvider (21 fields); methods: GetZoneID, ListRecords, CreateRecord, UpdateRecord (~1052 tok) +- `events_ws_e2e_test.go` β€” TestEventStreamPushesDeliveredMail, TestEventStreamRequiresUserSession (~1752 tok) +- `f2_complete_e2e_test.go` β€” TestF2CompleteSuiteEndToEnd (~4535 tok) +- `f3_complete_e2e_test.go` β€” fakeDnsProvider (56 fields); methods: GetZoneID, ListRecords, CreateRecord, UpdateRecord (~4487 tok) +- `http_services_e2e_test.go` β€” TestHttpServicesEndToEnd (~1425 tok) +- `imap_core_e2e_test.go` β€” TestImapCoreEndToEnd (~3244 tok) +- `imap_idle_condstore_e2e_test.go` β€” TestImapIdleAndCondStoreEndToEnd (~2089 tok) +- `managesieve_e2e_test.go` β€” TestManageSieveEndToEnd (~1814 tok) +- `migrations_test.go` (~595 tok) +- `outbound_queue_e2e_test.go` β€” staticMXResolver (27 fields); methods: LookupMX (~1791 tok) +- `pop3_e2e_test.go` β€” TestPop3EndToEnd (~1926 tok) +- `ports_test.go` β€” TestEmbeddedPostgresPortsAreOutsideTheEphemeralRange, TestEmbeddedPostgresPortsAreUnique, TestEachTestConnectsToThePortItStarted (~989 tok) +- `smtp_inbound_e2e_test.go` β€” contains end-to-end validation of the F0/F1 scope declared in (~2808 tok) +- `submission_e2e_test.go` β€” plainAuthClient (68 fields); methods: Start, Next (~3375 tok) +- `webmail_api_e2e_test.go` β€” TestWebmailApiEndToEnd, TestWebmailApiRejectsCrossMailboxAccess, TestWebmailApiRequiresUserSession (~3572 tok) + +## services/protocols/internal/health/ + +- `health_test.go` β€” TestHandler_Live_AlwaysReturns200, TestHandler_Ready_Returns200WhenPingSucceeds, TestHandler_Ready_Returns503WhenPingFails (~334 tok) +- `health.go` β€” exposes liveness/readiness endpoints (PLAN.md section 11.5). (~205 tok) + +## services/protocols/internal/infrastructure/acme/ + +- `issuer.go` β€” implements mode B of PLAN.md section 8.3: LambdaMail obtains (~2133 tok) +- `manager.go` β€” Interface: TlsaPublisher (6 methods) (~2176 tok) + +## services/protocols/internal/infrastructure/arc/ + +- `canonical.go` β€” implements Authenticated Received Chain (RFC 8617). (~1334 tok) +- `lazy_sealer_test.go` β€” stubKeyRepo (15 fields); methods: FindActiveKeys (~811 tok) +- `lazy_sealer.go` β€” LazySealer (18 fields); methods: Seal (~682 tok) +- `sealer_test.go` β€” TestSealer_StartsChainAtInstanceOneWithCvNone, TestSealer_WritesHeadersInPrescribedOrder, TestSealer_ProducesVerifiableChain, TestSealer_ExtendsExi... (~2324 tok) +- `sealer.go` β€” Sealer (56 fields); methods: WithClock, Seal (~2407 tok) +- `verifier.go` β€” VerifyChain (~1289 tok) + +## services/protocols/internal/infrastructure/clamav/ + +- `clamav_adapter_test.go` β€” TestClamAVAdapter_Scan_CleanAndVirus (~480 tok) +- `clamav_adapter.go` β€” ClamAVAdapter (28 fields); methods: Scan (~745 tok) + +## services/protocols/internal/infrastructure/cloudflare/ + +- `cloudflare_adapter_test.go` β€” TestCloudflareAdapter_GetZoneID_List_Create_Update_Delete (~835 tok) +- `cloudflare_adapter.go` β€” CloudflareAdapter (115 fields); methods: GetZoneID, ListRecords, CreateRecord, UpdateRecord (~3382 tok) +- `cloudflare_hardening_test.go` β€” TestCloudflareAdapter_CreateRecord_SendsProxiedFalseExplicitly, TestCloudflareAdapter_ReportsHttpErrorStatus, TestCloudflareAdapter_ListRecords_Fol... (~2754 tok) + +## services/protocols/internal/infrastructure/diskstorage/ + +- `local_disk_blob_reader_test.go` β€” TestLocalDiskBlobReader_ReadByID_ReturnsStoredContent, TestLocalDiskBlobReader_ReadByID_ReturnsErrorForUnknownID (~327 tok) +- `local_disk_blob_reader.go` β€” LocalDiskBlobReader (7 fields); methods: ReadByID (~248 tok) +- `local_disk_blob_storage_test.go` β€” TestLocalDiskBlobStorage_Store_WritesFileAndInsertsBlobRow, TestLocalDiskBlobStorage_Store_DedupsIdenticalContent (~676 tok) +- `local_disk_blob_storage.go` β€” implements port.BlobStorage on the local filesystem. (~992 tok) + +## services/protocols/internal/infrastructure/dkim/ + +- `keygen.go` β€” generates, stores and applies the DKIM signing keys described (~981 tok) +- `signer_test.go` β€” fakeKeyRepo (20 fields); methods: FindActiveKeys (~1127 tok) +- `signer.go` β€” Signer (16 fields); methods: Sign (~732 tok) + +## services/protocols/internal/infrastructure/mailauth/ + +- `authenticator_test.go` β€” fakeZone (34 fields); methods: LookupTXT, LookupIPAddr, LookupMX, LookupAddr (~2301 tok) +- `authenticator.go` β€” runs the inbound authentication chain of PLAN.md section (~1950 tok) + +## services/protocols/internal/infrastructure/netdns/ + +- `mx_resolver.go` β€” NetMXResolver (10 fields); methods: LookupMX (~197 tok) +- `public_verifier_match_test.go` β€” Comparing a desired record against what a resolver actually answers. (~1121 tok) +- `public_verifier.go` β€” PublicVerifier (35 fields); methods: VerifyRecord (~1718 tok) +- `tlsa_resolver.go` β€” TlsaResolver (14 fields); methods: LookupTLSA (~648 tok) + +## services/protocols/internal/infrastructure/postgres/ + +- `acme_repository.go` β€” AcmeRepository (64 fields); methods: LoadCertificate, SaveCertificate, LoadAccountKey, SaveAccountKey (~1995 tok) +- `alias_repository_test.go` β€” TestAliasRepository_EnsureSystemAliases (~342 tok) +- `alias_repository.go` β€” AliasRepository (15 fields); methods: EnsureSystemAliases (~388 tok) +- `attachment_backfill.go` β€” AttachmentBackfill (24 fields); methods: Done, MarkDone, Candidates, SetHasAttachments (~749 tok) +- `auth_repository_test.go` β€” TestAuthRepository_FindByAddress_ReturnsHashForActiveMailbox, TestAuthRepository_FindByAddress_ReturnsNilForUnknownAddress (~475 tok) +- `auth_repository.go` β€” AuthRepository (13 fields); methods: FindByAddress (~299 tok) +- `bimi_repository.go` β€” BimiRepository (24 fields); methods: Save, Load, Delete (~613 tok) +- `copy_repository_test.go` β€” TestCopyRepository_CopyMessages_AllocatesNewUIDsAndPreservesFlags (~1214 tok) +- `copy_repository.go` β€” CopyRepository (54 fields); methods: CopyMessages (~1610 tok) +- `delivered_report_store.go` β€” DeliveredReportStore (65 fields); methods: ListUningestedReports, MoveToReportsFolder (~1366 tok) +- `dkim_repository.go` β€” DkimRepository (73 fields); methods: FindActiveKeys, FindPublicKey, Insert, ListKeys (~2032 tok) +- `domain_repository.go` β€” DomainRepository (18 fields); methods: ActiveDomainNames, SaveDnsStatus (~711 tok) +- `domain_status_test.go` β€” dns_status is constrained by the schema to a fixed set. The verifier answers (~281 tok) +- `expunge_repository_test.go` β€” TestExpungeRepository_Expunge_SetsExpungedAtAndDecrementsCounters (~1063 tok) +- `expunge_repository.go` β€” ExpungeRepository (34 fields); methods: Expunge (~828 tok) +- `flag_repository_test.go` β€” TestFlagRepository_SetFlags_AddThenDelThenSet (~1329 tok) +- `flag_repository.go` β€” FlagRepository (45 fields); methods: SetFlags (~897 tok) +- `imap_folder_repository_test.go` β€” TestImapFolderRepository_FindByName_ReturnsFolderWithUIDData, TestImapFolderRepository_FindByName_ReturnsNilForUnknownFolder, TestImapFolderReposit... (~956 tok) +- `imap_folder_repository.go` β€” ImapFolderRepository (22 fields); methods: FindByName, ListFolders (~466 tok) +- `inbound_message_repository_test.go` β€” TestInboundMessageRepository_Persist_AllocatesSequentialUIDs, TestInboundMessageRepository_Persist_ReturnsErrorWhenNoInboxFolderExists, TestInbound... (~4003 tok) +- `inbound_message_repository.go` β€” InboundMessageRepository (55 fields); methods: PersistAll (~2136 tok) +- `mailbox_repository_test.go` β€” TestMailboxRepository_FindActiveByAddress_ReturnsNilForUnknownAddress, TestMailboxRepository_FindActiveByAddress_ReturnsRecordForSeededMailbox, Tes... (~3391 tok) +- `mailbox_repository.go` β€” MailboxRepository (33 fields); methods: FindActiveByAddress, ResolveDeliveryTargets (~699 tok) +- `message_query_repository_test.go` β€” TestMessageQueryRepository_ListMessages_ReturnsRowsOrderedByUID (~657 tok) +- `message_query_repository.go` β€” MessageQueryRepository (16 fields); methods: ListMessages (~366 tok) +- `outbound_repository.go` β€” OutboundRepository (41 fields); methods: Enqueue, FetchNextReady, CountRecipientsSince, UpdateJob (~1237 tok) +- `outbox_repository.go` β€” OutboxRepository (18 fields); methods: FetchUnpublished, MarkPublished (~415 tok) +- `pool.go` β€” contains pgx-based implementations of the application's (~120 tok) +- `report_ingestion_test.go` β€” dbBlobStore (59 fields); methods: Store (~3519 tok) +- `report_repository_test.go` β€” TestReportRepository_SaveDmarcAndTlsRpt (~406 tok) +- `report_repository.go` β€” ReportRepository (36 fields); methods: SaveDmarcReport, SaveTlsRptReport (~1023 tok) +- `sieve_repository_test.go` β€” TestSieveRepository_CRUD (~627 tok) +- `sieve_repository.go` β€” SieveRepository (55 fields); methods: GetScript, PutScript, ListScripts, SetActiveScript (~1245 tok) +- `vacation_log.go` β€” VacationLog (16 fields); methods: LastRepliedAt, RecordReply (~436 tok) +- `webmail_repository_test.go` β€” TestUnreadCountFollowsWhatWasActuallyRead, TestUnreadCountSelfHealsAfterDrift, TestOwnSentCopyIsNotCountedAsUnread, TestDeleteMovesToTrashThenExpun... (~2027 tok) +- `webmail_repository.go` β€” WebmailRepository (145 fields); methods: FindMailboxIDByAddress, ListFolders, ListMessages, GetMessageBlob (~4616 tok) + +## services/protocols/internal/infrastructure/rspamd/ + +- `rspamd_adapter_test.go` β€” TestRspamdAdapter_Scan_MapsActions (~482 tok) +- `rspamd_adapter.go` β€” RspamdResponse (27 fields); methods: Scan (~882 tok) + +## services/protocols/internal/infrastructure/tls/ + +- `acme_cert_watcher_test.go` β€” domain (38 fields) (~3205 tok) +- `acme_cert_watcher.go` β€” AcmeCertWatcher (73 fields); methods: Watch, EarliestExpiry, GetCertificate, LastReload (~2987 tok) +- `local_file_cert_provider_test.go` β€” TestNewLocalFileCertProvider_LoadsValidCertAndKey, TestNewLocalFileCertProvider_FailsFastOnMissingFiles, TestNewEphemeralSelfSignedCertProvider_Gen... (~874 tok) +- `local_file_cert_provider.go` β€” implements port.CertProvider by loading a static (~606 tok) +- `provider_status_test.go` β€” TestProviderStatus_ReportsAnEphemeralSelfSignedCertificate, TestProviderStatus_ReportsTheSpkiDigestOfTheServedCertificate (~846 tok) +- `provider_status.go` β€” ProviderStatus (22 fields); methods: HasCertificateFor, EarliestExpiry, LastReload, LastChange (~1114 tok) + +## services/protocols/internal/infrastructure/tlspolicy/ + +- `resolver_test.go` β€” rewriteTransport (31 fields); methods: RoundTrip, LookupTLSA (~2177 tok) +- `resolver.go` β€” resolves the outbound transport security policy of (~1949 tok) + +## services/protocols/internal/infrastructure/vault/ + +- `secret_vault_test.go` β€” TestSecretVault_SealOpenRoundTrip, TestSecretVault_SealIsNonDeterministic, TestSecretVault_DerivationIsStableAcrossInstances, TestSecretVault_Wrong... (~762 tok) +- `secret_vault.go` β€” seals the reusable secrets the system stores in Postgres - (~998 tok) + +## services/protocols/internal/presentation/http/ + +- `admin_dkim_api.go` β€” adminDkimAPI (33 fields); methods: SetAdminDkimAPI (~1675 tok) +- `admin_dns_api.go` β€” Interface: DnsRecordVerifier (21 methods) (~2650 tok) +- `admin_dns_reconcile_test.go` β€” stubReconciler (42 fields); methods: ReconcileDomain, SaveDnsStatus (~2445 tok) +- `admin_tls_api_test.go` β€” TestAcmeCertWatcherSatisfiesTlsStatusSource (~155 tok) +- `admin_tls_api.go` β€” Interface: TlsStatusSource (14 methods) (~1376 tok) +- `admin_tls_status_test.go` β€” stillSource (4 fields) (~513 tok) +- `attachment_test.go` β€” TestAttachmentDownload_DowngradesRenderableContentTypes, TestAttachmentDownload_KeepsHarmlessContentTypes, TestAttachmentDownload_NeutralisesHostil... (~937 tok) +- `bimi_api.go` β€” Interface: BimiStore (24 methods) (~2025 tok) +- `events_ws_test.go` β€” TestEventsReachOnlyTheirOwnMailbox, TestASlowSubscriberIsDroppedRatherThanBlocking, TestSubscriberCountFollowsAddAndRemove, TestPublishWithNoSubscr... (~1693 tok) +- `events_ws.go` β€” EventMessage (30 fields); methods: Publish, SubscriberCount, ServeHTTP, SetEventHub (~1490 tok) +- `mail_api_test.go` β€” fakeMailStore (58 fields); methods: FindMailboxIDByAddress, ListFolders, ListMessages, GetMessageBlob (~5364 tok) +- `mail_api.go` β€” HTTP handlers: writeJSON, writeError (~4631 tok) +- `router_test.go` β€” TestRouter_MtaSts_Autoconfig_SecurityTxt_Ingest, TestRouter_HealthReportsDegradedWithoutFailing, TestRouter_HealthFailsWhenDependencyIsDown (~1153 tok) +- `router.go` β€” Router (45 fields); methods: SetMtaStsMode, ServeHTTP, SetDegradedCheck, SetMailAPI (~3672 tok) +- `websession_test.go` β€” TestWebSessionVerifier_AcceptsTokenIssuedByTheAuthService, TestWebSessionVerifier_RejectsChallengeToken, TestWebSessionVerifier_RejectsWrongSecret,... (~1697 tok) +- `websession.go` β€” WebSession (29 fields); methods: Verify, RequireSurface (~934 tok) + +## services/protocols/internal/presentation/imap/ + +- `backend_test.go` β€” fakeAuthRepository (64 fields); methods: FindByAddress, ListFolders, FindByName, ListMessages (~6125 tok) +- `backend.go` β€” session (112 fields); methods: Close, Login, Select, Unselect (~4188 tok) +- `search_test.go` β€” TestSession_Search_MatchesByFlag, TestSession_Search_MatchesByUIDSetIntersection, TestSession_Search_MatchesByHeaderField, TestSession_Search_NotCr... (~1457 tok) +- `search.go` β€” Package imappresentation: this file implements IMAP SEARCH (RFC 3501 (~2395 tok) +- `stubs.go` β€” adapts github.com/emersion/go-imap/v2's (~373 tok) + +## services/protocols/internal/presentation/managesieve/ + +- `conn_test.go` β€” fakeAuthRepository (42 fields); methods: FindByAddress, GetScript, PutScript, ListScripts (~1681 tok) +- `conn.go` β€” conn (74 fields) (~2789 tok) +- `server.go` β€” Server (14 fields); methods: ListenAndServe, Serve, Close (~299 tok) + +## services/protocols/internal/presentation/pop3/ + +- `conn_test.go` β€” fakeAuthRepository (52 fields); methods: FindByAddress, ListFolders, FindByName, ListMessages (~2304 tok) +- `conn.go` β€” pop3Msg (82 fields) (~2806 tok) +- `server.go` β€” Server (14 fields); methods: ListenAndServe, Serve, Close (~293 tok) + +## services/protocols/internal/presentation/smtp/ + +- `backend_test.go` β€” fakeMailboxRepository (58 fields); methods: FindActiveByAddress, ResolveDeliveryTargets, Store, Scan (~3106 tok) +- `backend.go` β€” adapts github.com/emersion/go-smtp's Backend/ (~1399 tok) +- `sasl_login_test.go` β€” TestLoginServer_PromptsThenAuthenticates, TestLoginServer_AcceptsInitialResponse, TestLoginServer_PropagatesAuthFailure, TestLoginServer_RefusesExt... (~614 tok) +- `sasl_login.go` β€” loginServer (15 fields); methods: Next (~561 tok) +- `submission_backend_test.go` β€” TestSubmissionSession_RefusesCommandsBeforeAuth, TestSubmissionSession_RefusesForeignSender, TestSubmissionSession_AdvertisesPlainAndLogin, TestSub... (~816 tok) +- `submission_backend.go` β€” SubmissionBackend (29 fields); methods: NewSession, AuthMechanisms, Auth, Reset (~1300 tok) + +## services/rspamd/ + +- `Dockerfile` β€” Docker container definition (~147 tok) + +## services/storage/ + +- `Dockerfile` β€” Docker container definition (~466 tok) +- `package.json` β€” Node.js package manifest (~131 tok) +- `tsconfig.build.json` β€” TypeScript build configuration (~83 tok) +- `tsconfig.json` β€” TypeScript configuration (~88 tok) +- `vitest.config.ts` β€” Vitest test configuration (~33 tok) + +## services/storage/src/ + +- `health.test.ts` β€” Declares withServer (~470 tok) +- `health.ts` β€” Builds the /health/live and /health/ready handler shared by the auth and (~215 tok) +- `server.ts` β€” Declares port (~212 tok) + +## services/webmail/ + +- `Dockerfile` β€” Docker container definition (~395 tok) +- `next-env.d.ts` β€” / (~75 tok) +- `next.config.ts` β€” Next.js configuration (~601 tok) +- `package.json` β€” Node.js package manifest (~263 tok) +- `postcss.config.js` β€” PostCSS configuration (~24 tok) +- `tailwind.config.js` β€” Tailwind CSS configuration (~1064 tok) +- `tsconfig.json` β€” TypeScript configuration (~174 tok) +- `vitest.config.ts` β€” Vitest test configuration (~93 tok) + +## services/webmail/messages/ + +- `en.json` (~5338 tok) +- `es.json` (~5774 tok) +- `pt-BR.json` (~5763 tok) + +## services/webmail/public/ + +- `.gitkeep` (~0 tok) + +## services/webmail/src/ + +- `compose-draft-integrity.test.ts` β€” The composer's draft handling, and the delete action the mail list was (~1189 tok) +- `emailBody.test.ts` β€” The reader pane showed broken image icons for every message written by a (~1334 tok) +- `i18n.test.ts` β€” loadJson: getLeafKeys (~885 tok) +- `initials.test.ts` (~293 tok) +- `interface-integrity.test.ts` β€” Guards the class of defect this interface kept shipping: text that cannot be (~2690 tok) +- `mail-state-integrity.test.ts` β€” The wiring behind the counters, the reader pane and the two surfaces. (~2149 tok) +- `mailCounts.test.ts` β€” The counters the interface shows. (~4397 tok) +- `middleware.ts` β€” Gate for the two web surfaces. (~862 tok) +- `remoteImages.test.ts` β€” Whether a message's remote images load. (~1007 tok) +- `rules.test.ts` β€” Rules, as a person describes them, turned into Sieve and back. (~2014 tok) +- `sanitizer.test.ts` β€” These run under jsdom. Under the default node environment DOMPurify was (~1002 tok) +- `sidebarBadge.test.ts` β€” What the rail actually puts on screen, folder by folder. (~639 tok) +- `signature.test.ts` β€” The signature is edited in one place and injected into the composer in (~1022 tok) +- `surfaceGate.test.ts` β€” Which session, if any, a page needs before it may render. (~606 tok) +- `wellknown-routing.test.ts` β€” The well-known documents senders fetch over HTTPS. (~729 tok) + +## services/webmail/src/app/ + +- `globals.css` β€” Nocturne - the one visual language for both surfaces. (~2171 tok) +- `layout.tsx` β€” The locale is resolved on the server so the first paint is already in the (~356 tok) +- `page.tsx` β€” The root sends everyone straight into the mail client. (~213 tok) + +## services/webmail/src/app/(admin)/admin/ + +- `layout.tsx` β€” AdminLayout β€” uses useState (~2246 tok) + +## services/webmail/src/app/(admin)/admin/dashboard/ + +- `page.tsx` β€” Exactly the fields /api/v1/admin/dashboard returns - see dashboardStats(). (~2425 tok) + +## services/webmail/src/app/(admin)/admin/dmarc/ + +- `page.tsx` β€” AdminDmarcPage β€” renders table β€” uses useEffect (~1215 tok) + +## services/webmail/src/app/(admin)/admin/domains/ + +- `page.tsx` β€” Domains, read from the database. (~7639 tok) + +## services/webmail/src/app/(admin)/admin/mailboxes/ + +- `page.tsx` β€” The envelope every server-paged list answers with. (~12626 tok) + +## services/webmail/src/app/(admin)/admin/queue/ + +- `page.tsx` β€” The outbound queue, read from the database. (~2956 tok) + +## services/webmail/src/app/(admin)/admin/security/ + +- `page.tsx` β€” Live certificate state from the protocols service, which owns the cert (~6267 tok) + +## services/webmail/src/app/(admin)/admin/tests/ + +- `page.tsx` β€” Checks an operator runs by hand, against the server or one user. (~2274 tok) + +## services/webmail/src/app/(auth)/admin/login/ + +- `page.tsx` β€” AdminLoginPage β€” renders form β€” uses useState (~2803 tok) + +## services/webmail/src/app/(auth)/admin/step-up/ + +- `page.tsx` β€” Crossing from the webmail into the console. (~2187 tok) + +## services/webmail/src/app/(auth)/user/login/ + +- `page.tsx` β€” One step, because the mailbox is one step. (~1486 tok) + +## services/webmail/src/app/(user)/user/ + +- `layout.tsx` β€” Creates, renames or deletes one of the user's own folders. (~4684 tok) + +## services/webmail/src/app/(user)/user/compose/ + +- `page.tsx` β€” Splits a comma or semicolon separated field, dropping empty entries. (~5433 tok) + +## services/webmail/src/app/(user)/user/mail/[folder]/ + +- `page.tsx` β€” The initials shown in the sender avatar, from a display name or an address. (~10230 tok) + +## services/webmail/src/app/(user)/user/settings/ + +- `page.tsx` β€” Writes the whole rule list to the server as one script. (~12580 tok) + +## services/webmail/src/app/api/v1/[...path]/ + +- `route.ts` β€” Proxies /api/v1/* to the auth service. (~1250 tok) + +## services/webmail/src/app/health/ + +- `route.ts` β€” Liveness probe for the web container. (~210 tok) + +## services/webmail/src/components/ + +- `Avatar.tsx` β€” Rendered size in pixels; the square the circle is cut from. (~542 tok) +- `AvatarEditor.tsx` β€” The frame is square; the mask over it is what makes the photo a circle. (~2510 tok) +- `MessageReader.tsx` β€” The document the body frame renders, or null for a text-only message. (~4736 tok) +- `RecipientInput.tsx` β€” Entries as they will be sent: `Name ` or a bare address. (~1486 tok) +- `TotpEnrolment.tsx` β€” The scannable half of TOTP enrolment, shared by the webmail settings page and (~1644 tok) + +## services/webmail/src/components/ui/ + +- `Badge.tsx` β€” A tag: a small tinted label from the ramps. (~375 tok) +- `Button.tsx` β€” Nocturne buttons are outlined, not filled. (~584 tok) +- `Card.tsx` β€” A panel: a solid ground with a hairline inset ring. (~421 tok) +- `Pagination.tsx` β€” The pager under a server-paged list. (~672 tok) + +## services/webmail/src/i18n/ + +- `config.ts` β€” Picks the best supported locale from an Accept-Language header. (~993 tok) +- `LanguageSwitcher.tsx` β€” Language selector, rendered in both surface layouts (PLAN.md section 21.2). (~275 tok) +- `provider.tsx` β€” Shorthand for components that only need to translate. (~598 tok) + +## services/webmail/src/lib/ + +- `avatarCrop.test.ts` β€” Declares VIEWPORT (~1187 tok) +- `avatarCrop.ts` β€” The geometry behind the circular photo editor. (~1132 tok) +- `emailBody.ts` β€” Turns a message's sanitized HTML into something a frame can actually show. (~1181 tok) +- `formatBytes.test.ts` (~434 tok) +- `formatBytes.ts` β€” A byte count as a person reads it. (~398 tok) +- `initials.ts` β€” The two letters an avatar shows. (~236 tok) +- `mailCounts.ts` β€” The arithmetic behind every number the mail screens display. (~2786 tok) +- `paging.test.ts` β€” Declares w (~464 tok) +- `paging.ts` β€” The page numbers a pager should offer. (~273 tok) +- `remoteImages.ts` β€” Which senders may load their remote images. (~863 tok) +- `roleLabel.ts` β€” A role as a person reads it. (~200 tok) +- `rules.ts` β€” Rules as a person describes them, and the Sieve the server runs. (~1672 tok) +- `sanitizer.ts` β€” Sanitizes message HTML with DOMPurify (PLAN.md section 14.2). (~1142 tok) +- `signature.ts` β€” The signature, on its way from the settings screen into a message. (~1107 tok) +- `surfaceGate.ts` β€” Which session a page needs before it can usefully render. (~445 tok) +- `useAccount.ts` β€” Roles that may open the admin console. (~429 tok) +- `useDebounced.ts` β€” Holds a value still until typing stops. (~170 tok) +- `useFolders.ts` β€” The folder list, kept current. (~635 tok) +- `useMailEvents.ts` β€” Subscribes to the server's event stream and calls onEvent for each push. (~641 tok) diff --git a/.wolf/buglog.json b/.wolf/buglog.json new file mode 100644 index 0000000..419b908 --- /dev/null +++ b/.wolf/buglog.json @@ -0,0 +1,29 @@ +{ + "version": 1, + "bugs": [ + { + "error_message": "Email layout flattened: width/height/colspan/align/bgcolor stripped; background images never restored after 'show images'", + "root_cause": "Custom DOMPurify ALLOWED_URI_REGEXP replaced the default that accepts non-URL values, so every non-URL attribute was rejected; blockRemoteImages deleted url()/background irreversibly", + "fix": "Added DOMPurify's default non-URL alternatives to the regexp; block/unblock now rewrite backgrounds reversibly (lm-blocked- scheme, data-blocked-background)", + "tags": [ + "webmail", + "sanitizer", + "dompurify", + "remote-images" + ], + "date": "2026-10-13" + }, + { + "error_message": "Domain admin dashboard showed server-wide totals (all domains, queue, DMARC, storage)", + "root_cause": "dashboardStats, queueSummary, dmarcSummary, preflightSummary and the diagnostics queueByStatus took no AdminScope and queried every tenant; only listDomains/listMailboxes were scoped", + "fix": "Each now takes the session's AdminScope; mailboxScope() reaches outbound_jobs/email_messages through mailboxes.domain_id; DMARC matches by domain_id or name. Covered by services/auth/src/adminScope.test.ts against a real Postgres", + "tags": [ + "auth", + "authz", + "tenant-isolation", + "dashboard" + ], + "date": "2026-10-13" + } + ] +} \ No newline at end of file diff --git a/.wolf/cerebrum.md b/.wolf/cerebrum.md new file mode 100644 index 0000000..702c45b --- /dev/null +++ b/.wolf/cerebrum.md @@ -0,0 +1,27 @@ +--- +description: learned preferences, project conventions, and Do-Not-Repeat rules +budget_tokens: 2000 +--- +# Cerebrum + +> OpenWolf's learning memory. Updated automatically as the AI learns from interactions. +> Do not edit manually unless correcting an error. +> Last updated: 2026-10-06 + +## User Preferences + + + +## Key Learnings + +- **Project:** LambdaMail +- **Description:** A self-hosted mail server with an integrated webmail and admin console, built to + +## Do-Not-Repeat + + + + +## Decision Log + + diff --git a/.wolf/config.json b/.wolf/config.json new file mode 100644 index 0000000..8efa4f2 --- /dev/null +++ b/.wolf/config.json @@ -0,0 +1,121 @@ +{ + "version": 1, + "openwolf": { + "enabled": true, + "reads": { + "duplicate_mode": "warn", + "skeleton_hints": true + }, + "bash": { + "filter_mode": "suggest", + "governor": { + "mode": "replace", + "threshold_tokens": 2000, + "families": { + "grep_flood": "replace", + "file_print": "replace", + "git_show": "replace", + "test": "suggest", + "build": "suggest", + "unknown": "suggest" + } + } + }, + "anatomy": { + "auto_scan_on_init": true, + "rescan_interval_hours": 6, + "max_description_length": 100, + "max_files": 500, + "exclude_patterns": [ + "node_modules", + "dist", + "build", + ".next", + ".nuxt", + ".turbo", + ".vercel", + ".netlify", + ".output", + "coverage", + "__pycache__", + ".venv", + "venv", + "site-packages", + ".pytest_cache", + ".mypy_cache", + ".ruff_cache", + ".tox", + "*.pyc", + ".gradle", + "target", + ".m2", + ".cargo", + ".git", + ".wolf", + ".cache", + ".vscode", + ".idea", + ".DS_Store", + "Thumbs.db", + "*.min.js", + "*.min.css", + "*.map" + ], + "respect_gitignore": true + }, + "token_audit": { + "enabled": true, + "report_frequency": "weekly", + "waste_threshold_percent": 15, + "chars_per_token_code": 3.5, + "chars_per_token_prose": 4 + }, + "cron": { + "enabled": true, + "max_retry_attempts": 3, + "dead_letter_enabled": true, + "heartbeat_interval_minutes": 30 + }, + "memory": { + "consolidation_after_days": 7, + "max_entries_before_consolidation": 200 + }, + "cerebrum": { + "max_tokens": 2000, + "reflection_frequency": "weekly" + }, + "daemon": { + "port": 18810, + "log_level": "info" + }, + "dashboard": { + "enabled": true, + "port": 18811, + "host": "127.0.0.1" + }, + "context": { + "session_digest_budget_tokens": 1500, + "reinjection_interval": 25, + "state_budgets": { + ".wolf/cerebrum.md": 2000, + ".wolf/STATUS.md": 1000 + }, + "budgets": { + "claude": 1500, + "codex": 1200, + "gemini": 1200, + "opencode": 1200, + "cursor": 800 + } + }, + "visibility": { + "mode": "quiet" + }, + "updates": { + "mode": "compatible" + }, + "agents": [ + "claude" + ] + } +} \ No newline at end of file diff --git a/.wolf/cron-manifest.json b/.wolf/cron-manifest.json new file mode 100644 index 0000000..7e58c5f --- /dev/null +++ b/.wolf/cron-manifest.json @@ -0,0 +1,66 @@ +{ + "version": 1, + "tasks": [ + { + "id": "anatomy-rescan", + "name": "Full anatomy rescan", + "schedule": "0 */6 * * *", + "description": "Re-scans project filesystem and reconciles anatomy.md", + "action": { + "type": "scan_project" + }, + "retry": { + "max_attempts": 3, + "backoff": "exponential", + "base_delay_seconds": 30 + }, + "failsafe": { + "on_failure": "log_and_continue", + "alert_after_consecutive_failures": 2, + "dead_letter": true + }, + "enabled": true + }, + { + "id": "memory-consolidation", + "name": "Consolidate old memory", + "schedule": "0 2 * * *", + "description": "Compress memory.md entries older than 7 days", + "action": { + "type": "consolidate_memory", + "params": { + "older_than_days": 7 + } + }, + "retry": { + "max_attempts": 2, + "backoff": "exponential", + "base_delay_seconds": 60 + }, + "failsafe": { + "on_failure": "skip_and_retry_next_cycle", + "dead_letter": false + }, + "enabled": true + }, + { + "id": "token-audit", + "name": "Token audit report", + "schedule": "0 0 * * 1", + "description": "Weekly waste pattern detection", + "action": { + "type": "generate_token_report" + }, + "retry": { + "max_attempts": 2, + "backoff": "linear", + "base_delay_seconds": 60 + }, + "failsafe": { + "on_failure": "log_and_continue", + "dead_letter": true + }, + "enabled": true + } + ] +} diff --git a/.wolf/memory.md b/.wolf/memory.md new file mode 100644 index 0000000..ad75938 --- /dev/null +++ b/.wolf/memory.md @@ -0,0 +1,33 @@ +--- +description: chronological action log per session, consolidated weekly +--- +# Memory + +> Chronological action log. Hooks and AI append to this file automatically. +> Old sessions are consolidated by the daemon weekly. + +## Session: 2026-10-06 08:07 + +| Time | Action | File(s) | Outcome | ~Tokens | +|------|--------|---------|---------|--------| + +## Session: 2026-10-13 09:32 + +| Time | Action | File(s) | Outcome | ~Tokens | +|------|--------|---------|---------|--------| +| 09:40 | Created services/webmail/src/components/MessageReader.tsx | β€” | ~4736 | +| 09:51 | Created services/webmail/src/components/RecipientInput.tsx | β€” | ~1486 | + +| 2026-10-13T13:08:21.421Z | Webmail: MessageReader redesign (Outlook-style), sanitizer fix for layout attrs and reversible background blocking, chip recipients, reply-all, starred/important flag (Go /mail/flagged), high-importance send, resizable list/reader divider. Auth: domain-admin views scoped (dashboard, queue, DMARC, preflight, diagnostics). | services/webmail/src/components/MessageReader.tsx,services/webmail/src/components/RecipientInput.tsx,services/webmail/src/lib/recipients.ts,services/webmail/src/lib/paneWidth.ts,services/auth/src/repository.ts,services/auth/src/adminScope.test.ts,services/protocols/internal/infrastructure/postgres/webmail_repository.go | go, auth (incl. DB) and webmail suites pass; Coolify API token in .env returns 401 | +| 10:47 | Session end: 2 writes across 2 files (MessageReader.tsx, RecipientInput.tsx) | 2 reads | ~6222 tok | + +## Session: 2026-10-20 19:06 + +| Time | Action | File(s) | Outcome | ~Tokens | +|------|--------|---------|---------|--------| + +## Session: 2026-10-22 14:28 + +| Time | Action | File(s) | Outcome | ~Tokens | +|------|--------|---------|---------|--------| +| 14:41 | Created services/auth/src/accounts.ts | β€” | ~715 | diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..fa2baa9 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,5 @@ +# OpenWolf + +This project uses OpenWolf for context management. The always-on rules live in `.claude/rules/openwolf.md`; the hooks handle bookkeeping (anatomy index, memory log, read tracking) automatically. + +For the full operating protocol (session handoff, memory discipline, bug logging), load the `openwolf` skill, or read `.wolf/OPENWOLF.md`. Regenerate the session handoff with `/handoff`. diff --git a/migrations/0016_trusted_senders.down.sql b/migrations/0016_trusted_senders.down.sql new file mode 100644 index 0000000..4a76178 --- /dev/null +++ b/migrations/0016_trusted_senders.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS trusted_senders; diff --git a/migrations/0016_trusted_senders.up.sql b/migrations/0016_trusted_senders.up.sql new file mode 100644 index 0000000..1a97bbe --- /dev/null +++ b/migrations/0016_trusted_senders.up.sql @@ -0,0 +1,14 @@ +-- Senders a mailbox has said it trusts. +-- +-- Two things hang off one decision: mail from a trusted sender is not filed as +-- spam, and its remote images load without asking. Both used to be forgotten: +-- moving a message out of Junk taught nothing, so the next reply from the same +-- person went to Junk again, and the choice to show images lived in one +-- browser's storage. +CREATE TABLE trusted_senders ( + mailbox_id UUID NOT NULL REFERENCES mailboxes(id) ON DELETE CASCADE, + -- Lowercased, so one person is one row however they spell their address. + address VARCHAR(320) NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + PRIMARY KEY (mailbox_id, address) +); diff --git a/services/auth/src/repository.ts b/services/auth/src/repository.ts index b43267a..406bf84 100644 --- a/services/auth/src/repository.ts +++ b/services/auth/src/repository.ts @@ -1365,3 +1365,41 @@ export async function traceLogsByQueueId(queueId: string): Promise { ); } + +// ---------------------------------------------------------- trusted senders + +/** A mailbox cannot grow this list without bound. */ +export const MAX_TRUSTED_SENDERS = 5000; + +/** The address an input resolves to, or "" when it is not one. */ +export function normaliseSender(raw: string): string { + const angled = raw.match(/<([^>]+)>/); + const address = (angled ? angled[1] : raw).trim().toLowerCase(); + return address.length <= 320 && /^[^\s@<>]+@[^\s@<>]+$/.test(address) ? address : ""; +} + +export async function listTrustedSenders(mailboxId: string): Promise { + const rows = await query<{ address: string }>( + `SELECT address FROM trusted_senders WHERE mailbox_id = $1 ORDER BY address`, + [mailboxId], + ); + return rows.map((row) => row.address); +} + +/** Returns false when the list is full; trusting someone already trusted is a no-op. */ +export async function addTrustedSender(mailboxId: string, address: string): Promise { + const rows = await query<{ n: string }>( + `SELECT COUNT(*) AS n FROM trusted_senders WHERE mailbox_id = $1`, + [mailboxId], + ); + if (Number(rows[0]?.n ?? 0) >= MAX_TRUSTED_SENDERS) return false; + await query( + `INSERT INTO trusted_senders (mailbox_id, address) VALUES ($1, $2) ON CONFLICT DO NOTHING`, + [mailboxId, address], + ); + return true; +} + +export async function removeTrustedSender(mailboxId: string, address: string): Promise { + await query(`DELETE FROM trusted_senders WHERE mailbox_id = $1 AND address = $2`, [mailboxId, address]); +} diff --git a/services/auth/src/router.test.ts b/services/auth/src/router.test.ts index dcd7f7b..faab669 100644 --- a/services/auth/src/router.test.ts +++ b/services/auth/src/router.test.ts @@ -1194,6 +1194,47 @@ describeDb("moving between the surfaces", () => { expect(first.body.token).not.toBe(second.body.token); }, 60000); + describe("trusted senders", () => { + let token = ""; + beforeAll(async () => { + token = (await post("/api/v1/auth/user/login", { email: plainEmail(), password: PASSWORD })).body.token as unknown as string; + }, 60000); + + const send = async (method: string, path: string, body?: unknown) => { + const res = await fetch(`${base}${path}`, { + method, + headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" }, + ...(body === undefined ? {} : { body: JSON.stringify(body) }), + }); + return { status: res.status, body: (await res.json().catch(() => ({}))) as Record }; + }; + + it("remembers a sender once however it is spelled, and forgets it on request", async () => { + expect((await send("PUT", "/api/v1/user/trusted-senders", { address: "Jenny " })).status).toBe(200); + expect((await send("PUT", "/api/v1/user/trusted-senders", { address: "jenny@example.test" })).status).toBe(200); + expect((await send("GET", "/api/v1/user/trusted-senders")).body.senders).toEqual(["jenny@example.test"]); + + expect((await send("DELETE", "/api/v1/user/trusted-senders?address=JENNY@example.test")).status).toBe(200); + expect((await send("GET", "/api/v1/user/trusted-senders")).body.senders).toEqual([]); + }, 60000); + + it("refuses something that is not an address", async () => { + expect((await send("PUT", "/api/v1/user/trusted-senders", { address: "not an address" })).status).toBe(400); + expect((await send("PUT", "/api/v1/user/trusted-senders", {})).status).toBe(400); + }, 60000); + + it("keeps one mailbox's list from another's", async () => { + await send("PUT", "/api/v1/user/trusted-senders", { address: "private@example.test" }); + const other = (await post("/api/v1/auth/user/login", { email: userEmail(), password: PASSWORD })).body.token as unknown as string; + const res = await fetch(`${base}/api/v1/user/trusted-senders`, { headers: { Authorization: `Bearer ${other}` } }); + expect(((await res.json()) as { senders: string[] }).senders).not.toContain("private@example.test"); + }, 60000); + + it("needs a session", async () => { + expect((await get("/api/v1/user/trusted-senders")).status).toBe(401); + }, 60000); + }); + // Several webmail accounts at once, and a session that renews itself. describe("several accounts and sliding sessions", () => { const emailOf = (local: string) => `${local}@authtest-${DOMAIN_ID}.invalid`; diff --git a/services/auth/src/router.ts b/services/auth/src/router.ts index ad10cd0..4493e72 100644 --- a/services/auth/src/router.ts +++ b/services/auth/src/router.ts @@ -173,6 +173,9 @@ async function route(req: IncomingMessage, res: ServerResponse, url: string): Pr } if (url === "/api/v1/user/preferences" && method === "GET") return userGetPreferences(res, session!); if (url === "/api/v1/user/preferences" && method === "POST") return userUpdatePreferences(req, res, session!); + if (url === "/api/v1/user/trusted-senders" && method === "GET") return trustedSendersList(res, session!); + if (url === "/api/v1/user/trusted-senders" && method === "PUT") return trustedSenderAdd(req, res, session!); + if (url === "/api/v1/user/trusted-senders" && method === "DELETE") return trustedSenderRemove(req, res, session!); if (url === "/api/v1/user/sieve" && method === "GET") return userGetSieve(res, session!); if (url === "/api/v1/user/sieve" && method === "POST") return userSaveSieve(req, res, session!); if (url === "/api/v1/user/vacation" && method === "POST") return userSaveVacation(req, res, session!); @@ -1195,6 +1198,28 @@ async function userGetPreferences(res: ServerResponse, session: SessionTokenPayl sendJson(res, 200, await repo.getUserPreferences(session.sub)); } +async function trustedSendersList(res: ServerResponse, session: SessionTokenPayload): Promise { + sendJson(res, 200, { senders: await repo.listTrustedSenders(session.sub) }); +} + +async function trustedSenderAdd(req: IncomingMessage, res: ServerResponse, session: SessionTokenPayload): Promise { + const body = await parseJsonBody(req); + const address = repo.normaliseSender(typeof body?.address === "string" ? body.address : ""); + if (!address) return sendJson(res, 400, { error: "INVALID_INPUT", message: "A valid email address is required" }); + if (!(await repo.addTrustedSender(session.sub, address))) { + return sendJson(res, 409, { error: "LIMIT_REACHED", message: "The trusted senders list is full" }); + } + sendJson(res, 200, { ok: true, address }); +} + +async function trustedSenderRemove(req: IncomingMessage, res: ServerResponse, session: SessionTokenPayload): Promise { + const raw = new URL(req.url || "/", "http://localhost").searchParams.get("address") ?? ""; + const address = repo.normaliseSender(raw); + if (!address) return sendJson(res, 400, { error: "INVALID_INPUT", message: "A valid email address is required" }); + await repo.removeTrustedSender(session.sub, address); + sendJson(res, 200, { ok: true }); +} + async function userUpdatePreferences(req: IncomingMessage, res: ServerResponse, session: SessionTokenPayload): Promise { const body = await parseJsonBody(req); const signature = typeof body?.signature === "string" ? body.signature : ""; diff --git a/services/protocols/cmd/lambdamail-protocols/main.go b/services/protocols/cmd/lambdamail-protocols/main.go index 52d759f..056fe25 100644 --- a/services/protocols/cmd/lambdamail-protocols/main.go +++ b/services/protocols/cmd/lambdamail-protocols/main.go @@ -135,6 +135,8 @@ func run(cfg config) { // wired the scripts were stored and never read, so a vacation responder // that was switched on never replied and no filter ever filed anything. inboundUC.SetRules(usecase.NewApplyRulesUseCase(postgres.NewSieveRepository(pool))) + // Senders the mailbox trusts are not filed as spam. + inboundUC.SetTrustedSenders(postgres.NewTrustedSenders(pool)) // ------------------------------------------------------------ outbound var signer port.DkimSigner diff --git a/services/protocols/internal/application/port/trusted_senders.go b/services/protocols/internal/application/port/trusted_senders.go new file mode 100644 index 0000000..c8e306f --- /dev/null +++ b/services/protocols/internal/application/port/trusted_senders.go @@ -0,0 +1,18 @@ +package port + +import ( + "context" + + "github.com/google/uuid" +) + +// TrustedSenders is the list of senders a mailbox has said it trusts. +// +// Mail from one of them is not filed as spam, which is what makes "this is not +// spam" mean something: before, moving a message out of Junk taught nothing and +// the next reply from the same person went straight back. +type TrustedSenders interface { + // IsTrusted reports whether the mailbox trusts this address, which is + // already lowercased. + IsTrusted(ctx context.Context, mailboxID uuid.UUID, address string) (bool, error) +} diff --git a/services/protocols/internal/application/usecase/message_headers.go b/services/protocols/internal/application/usecase/message_headers.go index 788e685..65c685e 100644 --- a/services/protocols/internal/application/usecase/message_headers.go +++ b/services/protocols/internal/application/usecase/message_headers.go @@ -18,6 +18,8 @@ const snippetLength = 200 type MessageHeaders struct { Subject string FromDisplayName string + // FromAddress is the address in From, lowercased, or empty when it does not parse. + FromAddress string MessageID string Snippet string HasAttachments bool @@ -58,6 +60,7 @@ func ExtractMessageHeaders(payload []byte) MessageHeaders { if from := msg.Header.Get("From"); from != "" { if addr, err := mail.ParseAddress(from); err == nil { + out.FromAddress = strings.ToLower(addr.Address) out.FromDisplayName = truncateUTF8(decodeHeader(addr.Name), 255) } else { // A From that does not parse still often carries a usable name diff --git a/services/protocols/internal/application/usecase/process_inbound_email.go b/services/protocols/internal/application/usecase/process_inbound_email.go index 325c695..a341112 100644 --- a/services/protocols/internal/application/usecase/process_inbound_email.go +++ b/services/protocols/internal/application/usecase/process_inbound_email.go @@ -67,6 +67,7 @@ type ProcessInboundEmailUseCase struct { trackerManager *MailboxTrackerManager reportIngestor *IngestDeliveredReportsUseCase rules *ApplyRulesUseCase + trusted port.TrustedSenders submission *ProcessOutboundEmailUseCase auth port.AuthRepository replyHost string @@ -294,6 +295,11 @@ func (uc *ProcessInboundEmailUseCase) Handle(ctx context.Context, input ProcessI // Each recipient's own rules decide their own copy: two mailboxes on // this server receiving the same message may file it differently. folder := targetFolder + // Before the rules, so a filter still has the last word: the spam + // verdict is the filter's guess and the rules are the mailbox's own. + if folder == "Junk" && uc.rescuedFromJunk(ctx, recipient.ID, headers.FromAddress, input.Sender, authResult) { + folder = "INBOX" + } var decision RuleDecision if uc.rules != nil { decision = uc.rules.For(ctx, recipient.ID, input.RecipientAddresses[i], input.Sender, payload) diff --git a/services/protocols/internal/application/usecase/trusted_delivery.go b/services/protocols/internal/application/usecase/trusted_delivery.go new file mode 100644 index 0000000..38d8c7b --- /dev/null +++ b/services/protocols/internal/application/usecase/trusted_delivery.go @@ -0,0 +1,65 @@ +package usecase + +import ( + "context" + "log" + "strings" + + "github.com/google/uuid" + + "lambdamail/protocols/internal/application/port" +) + +// SetTrustedSenders lets a mailbox's trusted senders skip the spam folder. +// Optional: without it a spam verdict files the message in Junk for everyone. +func (uc *ProcessInboundEmailUseCase) SetTrustedSenders(trusted port.TrustedSenders) { + uc.trusted = trusted +} + +// rescuedFromJunk reports whether a message the spam filter condemned should +// reach the inbox because this mailbox trusts who it is from. +// +// The From header is what a person trusts, and it is also the easiest field to +// forge, so it only counts when the message proves it came from that address: +// DMARC passed, or SPF passed for the very domain written in From. Without the +// proof, trusting a contact would hand a spammer a way in by writing that +// contact's address on their mail. +// +// Fails closed: a lookup that errors leaves the message in Junk, which is where +// it was going anyway. +func (uc *ProcessInboundEmailUseCase) rescuedFromJunk( + ctx context.Context, + mailboxID uuid.UUID, + fromAddress, envelopeSender string, + auth port.InboundAuthResult, +) bool { + if uc.trusted == nil || fromAddress == "" { + return false + } + if !provesSender(fromAddress, envelopeSender, auth) { + return false + } + trusted, err := uc.trusted.IsTrusted(ctx, mailboxID, fromAddress) + if err != nil { + log.Printf("trusted senders: %v", err) + return false + } + return trusted +} + +func provesSender(fromAddress, envelopeSender string, auth port.InboundAuthResult) bool { + if auth.DMARC == port.AuthResultPass { + return true + } + return auth.SPF == port.AuthResultPass && + domainOf(fromAddress) != "" && + domainOf(fromAddress) == domainOf(normaliseAddress(envelopeSender)) +} + +func domainOf(address string) string { + at := strings.LastIndex(address, "@") + if at < 0 || at == len(address)-1 { + return "" + } + return address[at+1:] +} diff --git a/services/protocols/internal/application/usecase/trusted_delivery_test.go b/services/protocols/internal/application/usecase/trusted_delivery_test.go new file mode 100644 index 0000000..86ca777 --- /dev/null +++ b/services/protocols/internal/application/usecase/trusted_delivery_test.go @@ -0,0 +1,100 @@ +package usecase + +import ( + "bytes" + "context" + "errors" + "testing" + + "github.com/google/uuid" + + "lambdamail/protocols/internal/application/port" + "lambdamail/protocols/internal/domain/valueobject" +) + +type fakeTrusted struct { + trusted map[string]bool + err error +} + +func (f *fakeTrusted) IsTrusted(_ context.Context, _ uuid.UUID, address string) (bool, error) { + return f.trusted[address], f.err +} + +func deliverAsSpam(t *testing.T, trusted port.TrustedSenders, auth port.InboundAuthResult, envelope string) string { + t.Helper() + repo := &recordingMessageRepository{} + uc := NewProcessInboundEmailUseCase(nil, &capturingBlobStorage{}, repo) + uc.SetScanner(&stubScanner{result: &valueobject.ScanResult{Verdict: valueobject.ScanVerdictSpamJunk}}) + uc.SetAuthenticator(&stubAuthenticator{result: auth}) + if trusted != nil { + uc.SetTrustedSenders(trusted) + } + err := uc.Handle(context.Background(), ProcessInboundEmailInput{ + Sender: envelope, + Recipients: []port.MailboxRecord{{ID: uuid.New()}}, + RecipientAddresses: []string{"me@example.test"}, + Body: bytes.NewBufferString("From: Jenny \r\nSubject: re: hi\r\n\r\nbody"), + }) + if err != nil { + t.Fatalf("Handle: %v", err) + } + return repo.persisted[0].TargetFolderName +} + +var passDMARC = port.InboundAuthResult{SPF: "pass", DKIM: "pass", DMARC: "pass"} + +func TestTrustedSenderIsNotFiledAsSpam(t *testing.T) { + trusted := &fakeTrusted{trusted: map[string]bool{"jenny@friend.test": true}} + if got := deliverAsSpam(t, trusted, passDMARC, "jenny@friend.test"); got != "INBOX" { + t.Fatalf("a trusted, authenticated sender was filed in %q", got) + } +} + +func TestUntrustedSenderStillGoesToJunk(t *testing.T) { + trusted := &fakeTrusted{trusted: map[string]bool{"someone@else.test": true}} + if got := deliverAsSpam(t, trusted, passDMARC, "jenny@friend.test"); got != "Junk" { + t.Fatalf("an untrusted sender was filed in %q", got) + } +} + +// Writing a contact's address in From is all a spammer needs to do, so trust +// only counts when the message proves it came from there. +func TestForgedTrustedSenderStillGoesToJunk(t *testing.T) { + trusted := &fakeTrusted{trusted: map[string]bool{"jenny@friend.test": true}} + forged := port.InboundAuthResult{SPF: "fail", DKIM: "none", DMARC: "fail"} + if got := deliverAsSpam(t, trusted, forged, "bounce@spammer.test"); got != "Junk" { + t.Fatalf("a forged From reached the inbox: %q", got) + } +} + +// SPF that passed for the spammer's own domain proves nothing about From. +func TestSPFForAnotherDomainDoesNotProveFrom(t *testing.T) { + trusted := &fakeTrusted{trusted: map[string]bool{"jenny@friend.test": true}} + other := port.InboundAuthResult{SPF: "pass", DKIM: "none", DMARC: "none"} + if got := deliverAsSpam(t, trusted, other, "bounce@spammer.test"); got != "Junk" { + t.Fatalf("SPF for a different domain let a forged From in: %q", got) + } +} + +// A domain with no DMARC record can still be proven by SPF for its own domain. +func TestSPFForTheSameDomainProvesFrom(t *testing.T) { + trusted := &fakeTrusted{trusted: map[string]bool{"jenny@friend.test": true}} + spfOnly := port.InboundAuthResult{SPF: "pass", DKIM: "none", DMARC: "none"} + if got := deliverAsSpam(t, trusted, spfOnly, "mailer@friend.test"); got != "INBOX" { + t.Fatalf("SPF for the sender's own domain was not accepted: %q", got) + } +} + +func TestLookupFailureLeavesTheMessageInJunk(t *testing.T) { + trusted := &fakeTrusted{trusted: map[string]bool{"jenny@friend.test": true}, err: errors.New("db down")} + if got := deliverAsSpam(t, trusted, passDMARC, "jenny@friend.test"); got != "Junk" { + t.Fatalf("a failed lookup let mail through: %q", got) + } +} + +func TestWithoutTrustedSendersSpamGoesToJunk(t *testing.T) { + if got := deliverAsSpam(t, nil, passDMARC, "jenny@friend.test"); got != "Junk" { + t.Fatalf("filed in %q", got) + } +} diff --git a/services/protocols/internal/infrastructure/postgres/trusted_senders.go b/services/protocols/internal/infrastructure/postgres/trusted_senders.go new file mode 100644 index 0000000..112620f --- /dev/null +++ b/services/protocols/internal/infrastructure/postgres/trusted_senders.go @@ -0,0 +1,32 @@ +package postgres + +import ( + "context" + "fmt" + + "github.com/google/uuid" + "github.com/jackc/pgx/v5/pgxpool" +) + +// TrustedSenders reads the senders a mailbox trusts. The webmail writes them +// through the auth service; delivery only ever asks. +type TrustedSenders struct { + pool *pgxpool.Pool +} + +func NewTrustedSenders(pool *pgxpool.Pool) *TrustedSenders { + return &TrustedSenders{pool: pool} +} + +func (t *TrustedSenders) IsTrusted(ctx context.Context, mailboxID uuid.UUID, address string) (bool, error) { + var trusted bool + err := t.pool.QueryRow(ctx, ` + SELECT EXISTS ( + SELECT 1 FROM trusted_senders WHERE mailbox_id = $1 AND address = $2 + ) + `, mailboxID, address).Scan(&trusted) + if err != nil { + return false, fmt.Errorf("look up a trusted sender: %w", err) + } + return trusted, nil +} diff --git a/services/webmail/messages/en.json b/services/webmail/messages/en.json index bc7f22e..f96f547 100644 --- a/services/webmail/messages/en.json +++ b/services/webmail/messages/en.json @@ -321,9 +321,15 @@ "ccLabel": "Cc", "bccLabel": "Bcc", "hideImages": "Hide images", - "alwaysFromSender": "Always from this sender", + "alwaysFromSender": "Trust this sender", "forgetSender": "Stop trusting sender", - "moveTo": "Move to" + "moveTo": "Move to", + "notSpam": "Not spam", + "junkNotice": "This message is in Spam. Marking it as not spam moves it to the inbox and trusts this sender, so their next messages arrive there too.", + "trustSender": "Trust sender", + "trustedSender": "Trusted sender", + "trustSenderHint": "Never file this sender's mail as spam, and always show their images", + "trustedSenderHint": "Not filed as spam, images always shown. Click to stop trusting." }, "settings": { "actionDiscard": "Discard", diff --git a/services/webmail/messages/es.json b/services/webmail/messages/es.json index 5156653..2e48d66 100644 --- a/services/webmail/messages/es.json +++ b/services/webmail/messages/es.json @@ -321,9 +321,15 @@ "ccLabel": "Cc", "bccLabel": "Cco", "hideImages": "Ocultar imΓ‘genes", - "alwaysFromSender": "Siempre de este remitente", + "alwaysFromSender": "Confiar en este remitente", "forgetSender": "Dejar de confiar", - "moveTo": "Mover a" + "moveTo": "Mover a", + "notSpam": "No es spam", + "junkNotice": "Este mensaje estΓ‘ en Spam. Al marcarlo como no es spam, pasa a la bandeja de entrada y este remitente se vuelve de confianza, asΓ­ que sus prΓ³ximos mensajes llegarΓ‘n allΓ­ tambiΓ©n.", + "trustSender": "Confiar en el remitente", + "trustedSender": "Remitente de confianza", + "trustSenderHint": "No enviar nunca el correo de este remitente a spam y mostrar siempre sus imΓ‘genes", + "trustedSenderHint": "No va a spam y sus imΓ‘genes siempre se muestran. Haz clic para dejar de confiar." }, "settings": { "actionDiscard": "Descartar", diff --git a/services/webmail/messages/pt-BR.json b/services/webmail/messages/pt-BR.json index 3ae4f05..04e55e0 100644 --- a/services/webmail/messages/pt-BR.json +++ b/services/webmail/messages/pt-BR.json @@ -321,9 +321,15 @@ "ccLabel": "Cc", "bccLabel": "Cco", "hideImages": "Ocultar imagens", - "alwaysFromSender": "Sempre deste remetente", + "alwaysFromSender": "Confiar neste remetente", "forgetSender": "Deixar de confiar", - "moveTo": "Mover para" + "moveTo": "Mover para", + "notSpam": "NΓ£o Γ© spam", + "junkNotice": "Esta mensagem estΓ‘ no Spam. Ao marcar como nΓ£o Γ© spam, ela vai para a caixa de entrada e este remetente passa a ser confiΓ‘vel, entΓ£o as prΓ³ximas mensagens dele tambΓ©m chegam lΓ‘.", + "trustSender": "Confiar no remetente", + "trustedSender": "Remetente confiΓ‘vel", + "trustSenderHint": "Nunca mandar o e-mail deste remetente para o spam e sempre mostrar as imagens dele", + "trustedSenderHint": "NΓ£o vai para o spam e as imagens sempre aparecem. Clique para deixar de confiar." }, "settings": { "actionDiscard": "Descartar", diff --git a/services/webmail/src/app/(user)/user/mail/[folder]/page.tsx b/services/webmail/src/app/(user)/user/mail/[folder]/page.tsx index 32c69ba..3fc8d20 100644 --- a/services/webmail/src/app/(user)/user/mail/[folder]/page.tsx +++ b/services/webmail/src/app/(user)/user/mail/[folder]/page.tsx @@ -29,7 +29,7 @@ import { } from "../../../../../lib/mailCounts"; import { sanitizeEmailHtml, blockRemoteImages, unblockRemoteImages } from "../../../../../lib/sanitizer"; import { plainSnippet } from "../../../../../lib/snippet"; -import { isSenderTrusted, trustSender, revokeSender } from "../../../../../lib/remoteImages"; +import { useTrustedSenders } from "../../../../../lib/trustedSenders"; import { resolveInlineImages, buildReaderDocument, type InlineImages } from "../../../../../lib/emailBody"; import { Button } from "../../../../../components/ui/Button"; import { @@ -122,9 +122,9 @@ export default function MailFolderPage({ params }: { params: Promise<{ folder: s const [messages, setMessages] = useState([]); const [selected, setSelected] = useState(null); const [loadRemoteImages, setLoadRemoteImages] = useState(false); - // Whether this sender has a standing decision, so the banner can offer to - // take it back rather than only to grant it. - const [rememberSender, setRememberSender] = useState(false); + // The senders this account trusts, kept on the server so the decision + // follows the account to every browser and also reaches the spam filter. + const trustedSenders = useTrustedSenders(); const [searchQuery, setSearchQuery] = useState(""); const [activeFilter, setActiveFilter] = useState(null); const [loading, setLoading] = useState(true); @@ -333,7 +333,6 @@ export default function MailFolderPage({ params }: { params: Promise<{ folder: s return; } setLoadRemoteImages(false); - setRememberSender(false); setReaderHeight(420); try { const res = await fetch(`/api/v1/mail/message/${uid}?folder=${encodeURIComponent(folder)}`, { @@ -342,10 +341,6 @@ export default function MailFolderPage({ params }: { params: Promise<{ folder: s if (!res.ok) throw new Error(t("errors.serverError")); const message = await res.json(); setSelected(message); - // A sender the reader has already allowed does not get asked again. - const trusted = isSenderTrusted(message.from ?? ""); - setLoadRemoteImages(trusted); - setRememberSender(trusted); // Fetching the message is what marks it read on the server, so this only // brings the list and the badges into line with what already happened. void setSeen(uid, true, { persist: false }); @@ -362,6 +357,7 @@ export default function MailFolderPage({ params }: { params: Promise<{ folder: s searching: searchQuery.trim().length > 0, }); const selectedRow = messages.find((m) => m.uid === selected?.uid); + const senderTrusted = selected ? trustedSenders.isTrusted(selected.from ?? "") : false; /** * The document the reader frame renders. @@ -376,9 +372,12 @@ export default function MailFolderPage({ params }: { params: Promise<{ folder: s const sanitized = sanitizeEmailHtml(selected.html); if (!sanitized) return null; const withInline = resolveInlineImages(sanitized, selected.inline_images ?? {}); - const body = loadRemoteImages ? unblockRemoteImages(withInline) : blockRemoteImages(withInline); + // A trusted sender's images load without asking; anyone else's wait for + // the reader's choice, one message at a time. + const showImages = loadRemoteImages || trustedSenders.isTrusted(selected.from ?? ""); + const body = showImages ? unblockRemoteImages(withInline) : blockRemoteImages(withInline); return buildReaderDocument(body); - }, [selected, loadRemoteImages]); + }, [selected, loadRemoteImages, trustedSenders]); // Whether anything was actually held back, so the privacy banner is not shown // over a message that has no remote content at all. @@ -651,25 +650,29 @@ export default function MailFolderPage({ params }: { params: Promise<{ folder: s document={readerDocument} frameHeight={readerHeight} hasBlockedContent={hasBlockedContent} - imagesLoaded={loadRemoteImages} - senderRemembered={rememberSender} + imagesLoaded={loadRemoteImages || senderTrusted} + senderTrusted={senderTrusted} + isJunk={folder.toLowerCase() === "junk"} onClose={() => setSelected(null)} onToggleFlagged={() => void setFlagged(selected.uid, !(selectedRow?.flagged ?? false))} onToggleSeen={() => void setSeen(selected.uid, !(selectedRow?.seen ?? true), { persist: true })} onDelete={() => void deleteMessage(selected.uid)} onMove={(target) => void moveMessage(selected.uid, target)} onToggleImages={() => setLoadRemoteImages(!loadRemoteImages)} - onToggleRemember={() => { - if (rememberSender) { - revokeSender(selected.from); - setRememberSender(false); + onToggleTrusted={() => { + if (senderTrusted) { + void trustedSenders.revoke(selected.from); setLoadRemoteImages(false); } else { - trustSender(selected.from); - setRememberSender(true); - setLoadRemoteImages(true); + void trustedSenders.trust(selected.from); } }} + onNotSpam={() => { + // Two steps, one decision: trusting is what makes the next + // reply reach the inbox, moving this one is what is seen now. + void trustedSenders.trust(selected.from); + void moveMessage(selected.uid, "INBOX"); + }} /> ) : ( void; onToggleSeen: () => void; onToggleFlagged: () => void; onDelete: () => void; onMove: (target: string) => void; onToggleImages: () => void; - onToggleRemember: () => void; + onToggleTrusted: () => void; + onNotSpam: () => void; } /** The sender's name and address, from a header like `Jenny `. */ @@ -324,6 +330,22 @@ export function MessageReader(props: Props) { <{address}> )} +
+ +
{visibleTo.length > 0 && ( <> @@ -361,18 +383,35 @@ export function MessageReader(props: Props) { error. Two separate decisions - look now, or stop asking about this sender - so a reader can see one message without granting anything permanently. */} - {(props.hasBlockedContent || props.senderRemembered) && ( + {props.isJunk && ( +
+ + + {t("mail.junkNotice")} + + +
+ )} + + {props.hasBlockedContent && (
{props.imagesLoaded ? t("mail.imagesEnabled") : t("mail.remoteContentBlocked")} - - + {/* A trusted sender's images are shown for as long as they are + trusted, so hiding them here would do nothing. */} + {!props.senderTrusted && ( + + )}
)} diff --git a/services/webmail/src/lib/remoteImages.ts b/services/webmail/src/lib/remoteImages.ts index d71d776..2fb7989 100644 --- a/services/webmail/src/lib/remoteImages.ts +++ b/services/webmail/src/lib/remoteImages.ts @@ -43,7 +43,7 @@ export function senderKey(from: string): string { * reader to fail to render. Failing closed means falling back to blocked, * which is the safe direction. */ -function readTrusted(): string[] { +export function readTrusted(): string[] { try { const raw = window.localStorage.getItem(TRUSTED_SENDERS_KEY); if (!raw) return []; @@ -86,3 +86,12 @@ export function revokeSender(from: string): void { if (!key) return; writeTrusted(readTrusted().filter((entry) => entry !== key)); } + +/** Forgets the browser's own copy, once it has been moved to the server. */ +export function clearLocalTrusted(): void { + try { + window.localStorage.removeItem(TRUSTED_SENDERS_KEY); + } catch { + // Nothing to clear. + } +} diff --git a/services/webmail/src/lib/snippet.ts b/services/webmail/src/lib/snippet.ts index fd62ddf..8a30f1e 100644 --- a/services/webmail/src/lib/snippet.ts +++ b/services/webmail/src/lib/snippet.ts @@ -32,6 +32,9 @@ export function plainSnippet(raw: string | null | undefined): string { // A block boundary is a space, or "Hello,

You" would read "Hello,You". .replace(/<\/(?:p|div|li|tr|h[1-6])>|/gi, " ") .replace(/<(style|script|head)\b[^>]*>[\s\S]*?<\/\1>/gi, " ") - .replace(/<[^>]*>/g, ""); + .replace(/<[^>]*>/g, "") + // The server cuts the preview at a fixed length, which can leave the start + // of a tag with no end ("...open]*$/, ""); return decodeEntities(withoutTags).replace(/\s+/g, " ").trim(); } diff --git a/services/webmail/src/lib/trustedSenders.ts b/services/webmail/src/lib/trustedSenders.ts new file mode 100644 index 0000000..f7b04d2 --- /dev/null +++ b/services/webmail/src/lib/trustedSenders.ts @@ -0,0 +1,91 @@ +"use client"; + +import { useCallback, useEffect, useState } from "react"; +import { senderKey, readTrusted, clearLocalTrusted } from "./remoteImages"; + +/** + * The senders the signed-in account trusts, kept on the server. + * + * One decision does both jobs: mail from a trusted sender is not filed as + * spam, and its remote images load without asking. It used to be a list in one + * browser's storage, so another browser, a cleared cache or a private window + * asked again - and moving a message out of Junk taught the filter nothing. + * + * What an earlier version remembered in this browser is moved up the first + * time the list loads, so nobody has to decide a second time. + */ +export function useTrustedSenders() { + const [senders, setSenders] = useState>(new Set()); + const [ready, setReady] = useState(false); + + useEffect(() => { + let cancelled = false; + (async () => { + try { + const res = await fetch("/api/v1/user/trusted-senders", { cache: "no-store" }); + if (!res.ok) return; + const known = new Set(((await res.json()) as { senders: string[] }).senders); + + const local = readTrusted().filter((address) => !known.has(address)); + const moved = await Promise.all( + local.map((address) => + fetch("/api/v1/user/trusted-senders", { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ address }), + }).then((r) => r.ok), + ), + ); + local.forEach((address, i) => moved[i] && known.add(address)); + // Only cleared when every entry made it: a failure keeps the browser's + // copy, so the next load tries again. + if (moved.every(Boolean)) clearLocalTrusted(); + + if (!cancelled) setSenders(known); + } catch { + // Nothing is trusted this session, which is the safe direction. + } finally { + if (!cancelled) setReady(true); + } + })(); + return () => { + cancelled = true; + }; + }, []); + + const isTrusted = useCallback((from: string) => senders.has(senderKey(from)), [senders]); + + const change = useCallback(async (from: string, trusted: boolean): Promise => { + const address = senderKey(from); + if (!address) return false; + const apply = (on: boolean) => + setSenders((current) => { + const next = new Set(current); + if (on) next.add(address); + else next.delete(address); + return next; + }); + apply(trusted); + try { + const res = trusted + ? await fetch("/api/v1/user/trusted-senders", { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ address }), + }) + : await fetch(`/api/v1/user/trusted-senders?address=${encodeURIComponent(address)}`, { method: "DELETE" }); + if (!res.ok) throw new Error("rejected"); + return true; + } catch { + apply(!trusted); + return false; + } + }, []); + + return { + ready, + isTrusted, + trust: (from: string) => change(from, true), + revoke: (from: string) => change(from, false), + }; +} diff --git a/services/webmail/src/snippet.test.ts b/services/webmail/src/snippet.test.ts index 366e19b..3bb74de 100644 --- a/services/webmail/src/snippet.test.ts +++ b/services/webmail/src/snippet.test.ts @@ -16,6 +16,9 @@ describe("plainSnippet", () => { it("drops a stylesheet instead of previewing its rules", () => { expect(plainSnippet("

Hi

")).toBe("Hi"); }); + it("drops a tag the preview was cut in the middle of", () => { + expect(plainSnippet("

Open cyber model access { expect(plainSnippet("a &madeup; b")).toBe("a &madeup; b"); });