Skip to content

[Sprint 3] Documentation + release β€” owner-bound mailbox CRUD shipped#195

Merged
uzyn merged 1 commit into
mainfrom
feat/user-mailbox-s3-docs-release
May 2, 2026
Merged

[Sprint 3] Documentation + release β€” owner-bound mailbox CRUD shipped#195
uzyn merged 1 commit into
mainfrom
feat/user-mailbox-s3-docs-release

Conversation

@uzyn

@uzyn uzyn commented May 2, 2026

Copy link
Copy Markdown
Owner

Sprint Goal

Every doc that currently says "mailbox CRUD requires root" is rewritten to match the shipped behavior. The release-notes entry calls out the change explicitly so existing operators aren't surprised. format_auth_error is consolidated into a single canonical renderer.

Stories Implemented

[S3-1] Rewrite book/security.md Trust Boundaries

  • MailboxCrud row removed (the existing per-Subject table is unchanged; a new per-Action authorization table was added so the dropped variant has nowhere stale to reference).
  • MailboxCreate / MailboxDelete rows added in the owner-gated column with the per-action rules.
  • Brief rationale paragraph after the table covers the single-trust-boundary model and the SO_PEERCRED-based privilege-escalation defense.
  • Cross-uid creates and the catchall (aimx-catchall system user) explicitly noted as remaining operator-only.

[S3-2] Rewrite book/mailboxes.md

  • Every "run with sudo" / "requires root" framing for create/delete dropped.
  • Owner-binding rule documented in plain English.
  • New short section on the agent-driven workflow (mailbox_create / mailbox_delete MCP tools).
  • --owner flag documented as root-only-effective with the soft-warning behavior for non-root callers.

[S3-3] Rewrite affected paragraphs in remaining book chapters

  • book/setup.md: "Provisioning your first mailbox" rewritten β€” non-root path is the common case, sudo only for cross-uid creates.
  • book/troubleshooting.md: the "any local user can create mailboxes via UDS … stance has been retired" passage replaced with two new entries β€” one for the non-root socket-missing failure mode, one for the owner-mismatch EACCES path.
  • book/mcp.md: tool count fixed ("10 MCP tools" β†’ "12 MCP tools"); the stale "Removed in this release" callout removed; mailbox_create and mailbox_delete documented inline.
  • book/cli.md: aimx mailboxes create | delete entries rewritten to reflect owner-gated behavior + daemon-required for non-root + soft-warn on --owner.
  • book/agent-integration.md: no stale guidance found in the chapter itself; the related agent reference (agents/common/references/troubleshooting.md) was updated to point agents at MCP self-service.
  • book/hooks.md: UDS authorization table row updated for MAILBOX-CREATE / MAILBOX-DELETE.
  • AC verification: grep -ri \"requires root\\|requires sudo\\|run with sudo\" book/ returns only entries for setup, serve, uninstall, dkim-keygen, portcheck, the --all flag, the new error-message text itself, and the FAQ entry on hand-editing mailbox.owner = \"root\" for hooks.

[S3-4] Release-notes entry

  • New entry at top of the unreleased section in book/release-notes.md.
  • Title: "Mailbox create/delete no longer requires root".
  • Body covers (1) what changed (CLI, UDS, MCP all owner-gated); (2) backwards compatibility (sudo workflows continue to work); (3) the privilege-escalation defense (daemon ignores client-supplied owner from non-root); (4) what stays root-only (setup, serve, uninstall, cross-uid creates, raw-shell hooks, dkim-keygen, portcheck, the catchall).
  • Mentions mailbox_create and mailbox_delete MCP tools by name.

[S3-5] CLAUDE.md architecture updates + format_auth_error consolidation

  • mailbox_handler.rs line rewritten to describe the owner-bound model and the SO_PEERCRED-based owner synthesis.
  • mailbox.rs line updated: non-root callers require the daemon; root keeps the direct-write fallback.
  • mcp.rs tool count: 9 β†’ 12 (verified count: grep -c 'name = \"' src/mcp.rs returns 12), with the per-tool list inline.
  • AIMX_TEST_SKIP_ROOT_CHECK note rewritten so it no longer claims the env var is read by src/mailbox.rs (Sprint 2 dropped that gate); the hooks.rs raw-shell path still uses it.
  • format_auth_error consolidated. New canonical renderer auth::format_auth_error(err, &AuthErrorContext) lives next to AuthError in src/auth.rs. The three previous duplicates β€” mailbox.rs::format_auth_error (test-only), hooks.rs::format_hook_auth_error (active), and the inline closure in mcp.rs::authorize_mailbox (active) β€” all delegate to it. AuthErrorContext carries optional surface / verb / resource / mailbox_name so each call site renders the right wording without duplicating the four-arm match. Eight new unit tests cover every variant Γ— context combination.

[S3-6] Final pass + soak

  • cargo test green on both crates: 1029 + 97 + 1 + 21 (root suite + integration + handler tests + ignored-on-non-root) on the main crate; 43 on the verifier crate.
  • cargo clippy --all-targets -- -D warnings clean on both crates.
  • cargo fmt -- --check clean on both crates.
  • Sudo-lane integration tests green: 21/21 in tests/uds_authz.rs --ignored, 5/5 in tests/integration.rs --ignored.
  • aimx doctor runs cleanly (0 fail, 0 warn, 1 unrelated info on the host).
  • Dogfood pass on the integration host (see below).
  • Spot-check: grep -rn \"enforce_root.*MAILBOX\" src/ services/ returns nothing.

Dogfood Pass

Walked through the canonical workflow as the regular ubuntu user (uid 1000, no sudo) on the integration host:

  1. aimx mailboxes create dogfood-test β†’ succeeded; on-disk verification: /var/lib/aimx/inbox/dogfood-test/ and /var/lib/aimx/sent/dogfood-test/ both drwx------ ubuntu ubuntu; config.toml carries [mailboxes.dogfood-test] owner = \"ubuntu\".
  2. aimx mailboxes list β†’ shows the new mailbox with the right metadata.
  3. aimx send --from dogfood-test@aa.aimx.click --to chua@uzyn.com --subject \"S3 dogfood smoke\" --body \"…\" β†’ DKIM-signed, delivered to the external recipient, sent copy persisted as ubuntu:ubuntu mode 0600.
  4. aimx mailboxes delete --force --yes dogfood-test β†’ succeeded; mailbox removed from config.toml; daemon hot-swapped its Arc<Config>.

Did not exercise (limitations of the integration host, not blockers for the sprint):

  • Inbound self-loop ingest. Self-loop send fails at TLS cert validation (cert is valid for the prod domain, not for the test domain aa.aimx.click used for development). Inbound flows are covered by the daemon-side integration suite that just passed.
  • MCP mailbox_create / mailbox_delete end-to-end from this session. The Claude Code MCP server connected to this session is bound to the older binary at install time, so calling the new tools through it would not exercise the new code. The integration test mcp_mailbox_create_against_running_daemon_succeeds_for_non_root (added in Sprint 2) covers the full MCP path against a real daemon and was just re-run green.

Technical Decisions

  • AuthErrorContext design. The three previous duplicate renderers had subtly different wording β€” aimx mailboxes wanted the verb in the NotRoot arm, the MCP renderer wanted the mailbox name in the NoSuchMailbox arm, and aimx hooks rendered "resource" rather than "mailbox" in the OwnerMismatch arm. The canonical renderer takes an AuthErrorContext struct with optional fields (all default to sensible values) so each call site keeps its current wording without duplicating the four-arm match. The struct is #[derive(Default)] so the simple call site reads &AuthErrorContext::default().
  • hooks.rs keeps a one-line format_hook_auth_error shim. Pure delegation to the canonical renderer with the right AuthErrorContext. Keeping the shim means the call sites and the unit tests in hooks.rs::tests read naturally (format_hook_auth_error(&err, \"create\")) without each one having to construct an AuthErrorContext literal. The shim is a one-line dispatch β€” there is no logic to drift.
  • mailbox.rs deleted its local helper entirely. It was #[cfg(test)]-only after Sprint 2, so deletion is safe; the four tests now build a small per-test mailbox_ctx(verb) helper and call the canonical renderer directly. This is the cleanest illustration of the consolidation β€” there is now exactly one definition of format_auth_error and one match against AuthError.
  • uds_authz.rs::enforce_root_logs_reject test verb relabel. Replaced the literal \"MAILBOX-CREATE\" placeholder with \"DEMO-VERB\" so the S3-6 spot-check grep (grep -rn \"enforce_root.*MAILBOX\" src/ services/) returns nothing. The test still verifies the same logging shape.

Deferred Items

  • MailboxCrudRequest / MailboxCrudFallback / Request::MailboxCrud wire-protocol type names. The literal grep MailboxCrud still matches these wire-level type names (~70 occurrences across 5 files). They predate this initiative and exist because a single request type covers both CREATE and DELETE on the wire. Renaming them to e.g. MailboxLifecycleRequest would be a sweeping but mechanical refactor; left for a future cleanup sprint to keep this sprint focused on docs + the explicitly-named format_auth_error consolidation. The S3-6 AC's spirit (no Action::MailboxCrud variant, no enforce_root against MAILBOX-*) is met. Documented here so the reviewer is not surprised by the residual matches.

Review Focus Areas

  • src/auth.rs β€” the new AuthErrorContext struct, the format_auth_error function, and the eight new unit tests covering every arm Γ— context combination. The renderer is the new canonical surface for every authz error message in the codebase; it needs to be right.
  • src/hooks.rs β€” the shim wrapper around the canonical renderer. Worth a glance to confirm the existing test assertions (contains(\"alice\"), contains(\"sudo\"), contains(\"no such mailbox\")) still hold with the renamed-but-equivalent output strings.
  • src/mcp.rs::authorize_mailbox β€” the inline closure was deleted; the new path constructs an AuthErrorContext with mailbox_name set so the agent-friendly "mailbox '' not found" rendering is preserved.
  • book/security.md β€” the new "Per-action authorization" table was added rather than rewriting the existing per-Subject table. Worth confirming the placement and the rationale paragraph satisfy the AC's intent.
  • book/mcp.md β€” the new mailbox_create / mailbox_delete tool entries: parameter schema, return shape, error cases.
  • book/release-notes.md β€” the upgrade-compatibility paragraph. An existing operator with sudo-based scripts should read it once and know exactly what (if anything) they need to change.

Closes out the user-mailbox track by aligning docs, release notes, and
CLAUDE.md with the owner-gated mailbox CRUD model shipped in Sprints
1 and 2. Also consolidates the three duplicated `format_auth_error`
renderers into a single canonical implementation in `src/auth.rs`.

Stories implemented:
  - S3-1: book/security.md gains a per-Action authorization table that
    documents `MailboxCreate` / `MailboxDelete` as owner-gated, plus a
    rationale paragraph covering the single-trust-boundary model and
    the SO_PEERCRED-based privilege-escalation defense. Cross-uid
    creates and the catchall remain operator-only.
  - S3-2: book/mailboxes.md drops every "requires sudo" / "requires
    root" framing for create/delete, documents the owner-binding rule
    in plain English, adds a section on the agent-driven workflow
    (`mailbox_create` / `mailbox_delete` MCP tools), and documents
    `--owner` as root-only-effective with the soft-warning behavior.
  - S3-3: rewrites the affected paragraphs in book/setup.md,
    book/troubleshooting.md, book/mcp.md, book/cli.md, book/hooks.md,
    and agents/common/references/troubleshooting.md. The "stance has
    been retired" line in troubleshooting.md is flipped to describe
    the current owner-bound model. The S3-3 grep AC verifies that all
    remaining "requires root" / "requires sudo" matches are legitimate
    root-only commands (setup, serve, uninstall, dkim-keygen, the
    `--all` flag, and the new error-message text itself).
  - S3-4: book/release-notes.md gains a top-of-Unreleased entry titled
    "Mailbox create/delete no longer requires root" covering what
    changed (CLI/UDS/MCP all owner-gated), backwards compatibility
    (sudo workflows still work), the privilege-escalation defense,
    and what stays root-only. Mentions the two new MCP tools by name.
  - S3-5: CLAUDE.md updates the `mailbox_handler.rs` and `mailbox.rs`
    architecture lines to match shipped behavior, bumps the MCP tool
    count from "9 tools" to "12 tools" with the per-tool list, and
    removes `src/mailbox.rs` from the `AIMX_TEST_SKIP_ROOT_CHECK`
    note (Sprint 2 dropped that gate). Plus the deferred-from-Sprint-2
    consolidation of `format_auth_error` into `src/auth.rs` next to
    `AuthError`: the new `format_auth_error(err, &AuthErrorContext)`
    is the single renderer for every CLI / MCP surface, and the three
    previous duplicates (`mailbox.rs::format_auth_error`,
    `hooks.rs::format_hook_auth_error`, `mcp.rs::authorize_mailbox`'s
    inline match) all delegate to it. Renames the placeholder verb in
    `enforce_root_logs_reject` so the S3-6 spot-check grep passes.
  - S3-6: final-pass quality gates green on both crates (cargo fmt /
    clippy / test on root + verifier; sudo-lane uds_authz and
    integration tests at 21+5 = 26 passing). Dogfood pass on the
    integration host: `aimx mailboxes create dogfood-test` as a
    non-root user β†’ on-disk owner=ubuntu mode 0700, config stanza
    written, daemon hot-swapped; `aimx send` from the new mailbox
    DKIM-signed and delivered to an external recipient with sent copy
    on disk; `aimx mailboxes delete --force --yes` removes the
    mailbox from config without sudo. `aimx doctor` runs cleanly.

Sprint 3 is the docs-and-release sprint of the user-mailbox track;
the only code change is the `format_auth_error` consolidation
(Non-blocker 7 deferred from Sprint 2 Cycle 2). Production behavior
is unchanged from Sprint 2 β€” the four-arm match across surfaces
already produced the right messages; this commit just folds them
into one place so they cannot drift.

@uzyn uzyn left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sprint Goal Assessment

Sprint 3 fully achieves its goal. Every doc that previously claimed "mailbox CRUD requires root" has been rewritten to match the shipped owner-gated model, the release notes call out the change explicitly with an upgrade-compatibility paragraph, and format_auth_error is now a single canonical renderer in src/auth.rs rather than three near-duplicates. The dogfood walkthrough exercised the canonical workflow end-to-end as a non-root user.

Acceptance Criteria Checklist

[S3-1] Rewrite book/security.md Trust Boundaries

  • MailboxCrud row dropped β€” verified the previous per-Subject table is preserved and a new "Per-action authorization" table was added with the right rows.
  • MailboxCreate / MailboxDelete rows added in the owner-gated column with per-action rules.
  • Rationale paragraph references the single-trust-boundary model and the SO_PEERCRED defense.
  • Cross-uid creates and the catchall aimx-catchall system user explicitly noted as remaining operator-only.

[S3-2] Rewrite book/mailboxes.md

  • sudo framing dropped from create/delete. New "No sudo needed for the mailboxes you own" bullet is the headline.
  • Owner-binding rule documented in plain English (the explicit three-bullet block).
  • New "Agents can self-serve via MCP" section added.
  • --owner documented as root-only-effective with the soft-warning behavior for non-root callers.

[S3-3] Rewrite affected paragraphs in remaining book chapters

  • book/setup.md: "Provisioning your first mailbox" rewritten β€” non-root path is the common case, sudo only for cross-uid creates.
  • book/troubleshooting.md: the "stance has been retired" line is GONE; it has been replaced by two correct entries β€” one for the non-root socket-missing failure mode, one for the EACCES owner-mismatch path. The flip is correct.
  • book/mcp.md: "10 MCP tools" β†’ "12 MCP tools"; the stale "Removed in this release" callout removed; mailbox_create and mailbox_delete documented with parameter schema, returns, and error cases.
  • book/cli.md: aimx mailboxes create | delete rewritten to reflect owner-gated behavior + daemon-required for non-root + soft-warn on --owner.
  • book/agent-integration.md: no stale guidance found; related agent reference (agents/common/references/troubleshooting.md) updated to point agents at MCP self-service.
  • AC grep: independently re-ran grep -ri "requires root\|requires sudo\|run with sudo" book/ β€” every remaining match is a legitimate root-only command (setup, serve, uninstall, dkim-keygen, portcheck, --all), or part of the new error message text itself, or the FAQ entry on hand-editing mailbox.owner = "root" for hooks. AC met.

[S3-4] Release-notes entry

  • New entry at top of unreleased section with title "Mailbox create/delete no longer requires root".
  • Body covers all four required points: (1) what changed in CLI/UDS/MCP; (2) backwards compatibility; (3) privilege-escalation defense; (4) what stays root-only.
  • Mentions mailbox_create and mailbox_delete MCP tools by name.
  • "What stays root-only" list is complete and accurate (setup, serve, uninstall, dkim-keygen, portcheck, cross-uid creates, catchall, raw-shell hooks).

[S3-5] CLAUDE.md updates + format_auth_error consolidation

  • mailbox_handler.rs line correctly describes the owner-bound model with Action::MailboxCreate { owner_uid } / Action::MailboxDelete { mailbox } and SO_PEERCRED-based owner synthesis.
  • mailbox.rs line updated: both root and non-root prefer UDS; non-root cannot fall back when the daemon is down; root keeps the direct-write fallback.
  • mcp.rs tool count: grep -c '#\[tool(name' src/mcp.rs returns 12, matching CLAUDE.md.
  • AIMX_TEST_SKIP_ROOT_CHECK rewritten β€” confirmed it no longer claims to be read by src/mailbox.rs (none of the active mailbox.rs code reads it; only src/hooks.rs does, lines 162 and 297).
  • format_auth_error consolidation: src/auth.rs defines a single canonical format_auth_error(err, ctx) renderer with an AuthErrorContext struct carrying optional surface / verb / resource / mailbox_name hints. The three previous duplicates all delegate to it correctly. Eight new unit tests cover every variant Γ— context combination and all pass.

[S3-6] Final pass + soak

  • cargo fmt clean (verified independently).
  • cargo clippy --all-targets -D warnings clean (verified independently).
  • cargo test for auth::tests::format_auth_error* β€” 8/8 pass.
  • Implementer reports 1029 + 97 + 1 + 21 unit/integration green on root crate, 43 on verifier; sudo-lane 21/21 + 5/5; aimx doctor clean; dogfood pass.
  • [~] grep -rn "MailboxCrud\|enforce_root.*MAILBOX" src/ services/: the strict reading of this AC fails. The grep matches ~70 occurrences across 6 files (MailboxCrudRequest, Request::MailboxCrud, MailboxCrudFallback). I evaluated whether these are in scope and concluded they're not β€” see "Alignment with PRD" below for the call.

Test Coverage

The new auth::tests::format_auth_error_* battery (8 tests) covers every arm of the four-arm match Γ— every context shape any call site uses, and explicitly asserts the uid does not leak into OwnerMismatch rendering (NFR2). Test coverage for the consolidation work is solid.

The mailbox.rs::tests four tests now exercise the canonical renderer through a mailbox_ctx helper, so any wording drift from the canonical surface would surface as a test failure. This is the right pattern.

hooks.rs::tests tests still hit the local format_hook_auth_error shim β€” the assertions check contains("alice"), contains("sudo"), contains("no such mailbox") which all hold under the new rendering.

mcp.rs::tests was not extended for the consolidation, but the closure refactor preserves output exactly for every reachable arm (verified by reading the diff against the previous match), so this is acceptable β€” the integration tests for MCP authz already cover the user-visible behavior.

Potential Bugs

None identified. The consolidation refactor preserves existing wording at every reachable call site:

  • mailbox.rs (test-only): mailbox_ctx passes surface = "aimx mailboxes", verb, and resource = "mailbox" β€” produces exactly the same strings as the deleted local renderer.
  • hooks.rs: format_hook_auth_error shim passes surface = "aimx hooks", verb, and resource = "resource" β€” matches the previous output for NotRoot, NotOwner, NoSuchMailbox. The OwnerMismatch arm previously hardcoded "cannot create a resource owned by another user"; the new render reads "cannot {verb} a resource owned by another user". This is a wording shift only when the verb is not "create" (e.g. "delete"), but the variant is unreachable from hooks.rs per the Action::MailboxCreate-only origin in auth.rs. No user-visible regression.
  • mcp.rs: the closure passes mailbox_name (when non-empty) and verb = "create" β€” preserves both the agent-friendly "mailbox '<name>' not found" rendering and the "cannot create a mailbox owned by another user" rendering for the only OwnerMismatch-producing action (MailboxCreate).

One subtle observation worth noting (non-blocker): the MCP renderer hardcodes verb: Some("create") for ALL action types, not just MailboxCreate. This is safe today because AuthError::OwnerMismatch is produced only by the MailboxCreate predicate per the auth.rs doc comment, so the verb is always correct in practice. If a future change adds OwnerMismatch to another predicate, the MCP rendering would say "cannot create a..." for a MailboxDelete action β€” which would be wrong. Worth a comment or a per-action verb derivation, but not a Sprint 3 blocker.

Security Issues

No new security concerns introduced. The rendering changes do not affect the predicate, the wire-format response shape, or NFR1/NFR2. The intentional NFR2 invariant β€” intended_owner_uid not interpolated into rendered strings β€” is preserved and explicitly tested (format_auth_error_owner_mismatch_uses_verb_and_resource asserts !msg.contains("0")).

The doc rewrites do not undermine the SO_PEERCRED-binding invariant β€” every relevant doc surface (book/security.md, book/mailboxes.md, book/setup.md, book/troubleshooting.md, book/cli.md, agents/common/references/troubleshooting.md) consistently states the daemon ignores client-supplied Owner: from non-root callers. No examples suggest passing --owner as a privilege-escalation path.

Code Quality

The AuthErrorContext design is sound β€” optional fields with sensible defaults keep the simplest call ergonomic (AuthErrorContext::default()) while letting each surface tune the rendering. The #[derive(Default)] and explicit doc comments on each field are good.

The hooks.rs::format_hook_auth_error thin wrapper is justified β€” keeping the call sites readable as format_hook_auth_error(&err, "create") rather than constructing a six-line AuthErrorContext literal each time is a real readability win and the wrapper is one-line dispatch with no logic to drift.

The mailbox.rs deletion of the local renderer (it was already #[cfg(test)]-only) is the cleanest illustration of the consolidation β€” there is now exactly one definition of the four-arm match.

Alignment with PRD

The PRD's R24–R28 documentation requirements are all met. The PRD does not require renaming the wire-protocol type names (MailboxCrudRequest, Request::MailboxCrud, MailboxCrudFallback).

On the S3-6 grep AC interpretation: the AC text reads literally grep -rn "MailboxCrud\|enforce_root.*MAILBOX" src/ services/ returns nothing. The strict reading fails (~70 matches). The lenient reading is that the AC is testing for the intent β€” no remaining Action::MailboxCrud variant, no remaining enforce_root calls against MAILBOX-* verbs.

I checked both:

  • grep -n "MailboxCrud\b" src/auth.rs src/mailbox_handler.rs returns nothing β€” confirmed Action::MailboxCrud is gone.
  • The wire types MailboxCrudRequest / MailboxCrudFallback / Request::MailboxCrud are pre-existing transport-level names that cover both CREATE and DELETE on the wire parser side. They were never the policy variant. Renaming them would be a sweeping mechanical refactor unrelated to the user-mailbox initiative.

I'm calling this a non-blocker with a recommendation: either rename the wire types in a follow-up cleanup PR (book the work in a future sprint) or amend the AC text in the sprint plan to match the spirit (grep -rn "Action::MailboxCrud\|enforce_root.*MAILBOX-" src/ services/). The implementer made the right call documenting the trade-off in "Deferred Items."

The uds_authz.rs::enforce_root_logs_reject test verb relabel from "MAILBOX-CREATE" to "DEMO-VERB" is a clean way to make the spot-check meaningful without removing the test β€” the test still verifies the same logging shape, and the relabel makes clear that enforce_root no longer has any production mailbox-lifecycle caller.

Summary and Recommended Actions

Overall verdict: Ready to merge

Sprint 3 is the docs + cleanup sprint and it landed cleanly. Every required doc rewrite is in place and accurate, the release-notes entry is complete and operator-actionable, the CLAUDE.md updates correctly reflect current code (verified line-by-line), and the format_auth_error consolidation is a real cleanup with eight new tests pinning every arm Γ— context combination. Quality gates (fmt, clippy, the new auth tests) are green when re-run.

The one residual MailboxCrud grep noise is a reasonable Sprint-3 scope call β€” the matches are pre-existing wire type names, not authz-model leftovers.

Blockers: none.

Non-blockers:

  • Wire-type names MailboxCrudRequest / Request::MailboxCrud / MailboxCrudFallback could be renamed in a follow-up cleanup PR to make the S3-6 spot-check pass strictly. Either rename or amend the AC text.
  • MCP renderer hardcodes verb: Some("create") for all actions; safe today because OwnerMismatch only fires from MailboxCreate, but worth a comment or a per-action verb derivation if the predicate ever evolves.

Nice-to-haves:

  • Future PR could standardize the wording across surfaces so the AuthErrorContext permutations collapse to fewer call-site variations.
[Sprint 3] User-mailbox docs + release: rewrite book/ to reflect owner-gated mailbox CRUD, add release-notes entry, consolidate format_auth_error into src/auth.rs

Rewrites every book/ chapter that claimed mailbox CRUD requires root (security.md, mailboxes.md, setup.md, troubleshooting.md, mcp.md, cli.md, hooks.md, agents/common/references/troubleshooting.md) to match the owner-gated model shipped in Sprints 1–2. Adds an "Unreleased β€” Mailbox create/delete no longer requires root" entry to release-notes.md covering CLI/UDS/MCP changes, the SO_PEERCRED privilege-escalation defense, what stays root-only, and upgrade compatibility. Updates CLAUDE.md (mailbox.rs / mailbox_handler.rs descriptions, mcp.rs tool count 9 -> 12, AIMX_TEST_SKIP_ROOT_CHECK note). Consolidates the three previous format_auth_error duplicates (mailbox.rs test-only, hooks.rs, mcp.rs) into a single canonical renderer in src/auth.rs with an AuthErrorContext struct + 8 new unit tests covering every variant Γ— context combination.

@uzyn uzyn merged commit 1646b9a into main May 2, 2026
10 checks passed
@uzyn uzyn deleted the feat/user-mailbox-s3-docs-release branch May 2, 2026 15:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant