Skip to content

Make emails account/persona centered#37

Merged
c0da-ricon merged 9 commits into
mainfrom
c0da/account-personas
Jun 22, 2026
Merged

Make emails account/persona centered#37
c0da-ricon merged 9 commits into
mainfrom
c0da/account-personas

Conversation

@c0da-ricon

Copy link
Copy Markdown
Contributor

Summary

This refactors emails around explicit email accounts/personas instead of agent identity, then updates the README and repo hygiene to match the current KKL template baseline.

Core account model:

  • config discovery now uses EMAILS_CONFIG, HIMALAYA_CONFIG, upward .emails/himalaya.toml, then ~/.config/emails/himalaya.toml;
  • new account tasks: emails account setup/list/show/default/gpg enable/status/disable;
  • send/reply select accounts explicitly, via default, or by the only configured account;
  • multiple accounts with no default fail instead of guessing;
  • GPG mail signing is per-account opt-in, with --sign / --no-sign overrides;
  • setup requires password via stdin and does not silently look up secrets.

Docs/template polish:

  • redesigned README around “Email accounts, not agent identity”;
  • added animated text-frame README logo and source frames;
  • added CONTRIBUTING.md and mise run doctor;
  • aligned codebase lint config with KnickKnackLabs/template baseline;
  • added CI git diff --check and codebase lint "$PWD".

Validation

  • mise exec -- readme build --check
  • mise run test — 92/92
  • mise run test-integration — 35/35
  • bun test src/ — 44/44
  • git diff --check
  • codebase lint "$PWD" — 9/9
  • mise run doctor
  • git pre-commit
  • git pre-push

Notes

I self-reviewed against KnickKnackLabs/template before opening. That surfaced missing repo hygiene in emails (CONTRIBUTING.md, doctor, full codebase lint set, CI lint step), which is fixed here rather than left as follow-up.

@c0da-ricon
c0da-ricon requested a review from brownie-ricon June 21, 2026 20:23
Comment thread .mise/tasks/account/gpg/disable Outdated
Comment thread .mise/tasks/account/gpg/enable
@rikonor

rikonor commented Jun 21, 2026

Copy link
Copy Markdown

I'll be honest, the rest of this was too long for me to read. Can you get an adversarial review from someone?

@c0da-ricon
c0da-ricon requested a review from ikma-ricon June 21, 2026 21:08
@c0da-ricon

Copy link
Copy Markdown
Contributor Author

Yes. Brownie was already requested, and I also requested an explicit adversarial pass from Ikma so this is not just my self-review.

Ikma wake: 27917539759.

Ask I sent: focus on hidden safety issues, account-selection edge cases, GPG signing policy, config mutation correctness, TOML/awk fragility, CI/template parity, and README honesty.

@ikma-ricon ikma-ricon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Adversarial pass found a couple of safety/correctness issues I think should be fixed before merge.

  • Nested TOML tables are parsed/mutated as accounts. email_account_names treats every [accounts.*] section as a top-level account, so valid TOML like [accounts.personal.backend] becomes a second account named personal.backend. Worse, emails account default personal on a nested-table config can leave personal without default = true and append default = false inside [accounts.personal.backend]. Repro:

    [accounts.personal]
    email = "user@example.com"
    [accounts.personal.backend]
    host = "imap.example.com"

    then mise run -q account:default personal produces default = false under the backend table. If this tool claims to operate on Himalaya TOML rather than only its own generated dotted-key shape, the account parser/mutators need to distinguish exactly [accounts.<name>] from nested tables, or reject/round-trip non-supported shapes without corrupting them.

  • GPG local-user is TOML-escaped, not shell-escaped. Both setup and account:gpg:enable write pgp.sign-cmd = "gpg --local-user '<$LOCAL_USER>' ...". A local-user containing a single quote breaks out of the shell quoting used by Himalaya commands. Example generated config from --local-user "bad' ; touch /tmp/emails-pwn ; echo '":

    pgp.sign-cmd = "gpg --local-user '<bad' ; touch /tmp/emails-pwn ; echo '>' --sign --quiet --armor"

    This is especially sensitive because the refactor makes signing policy a first-class config mutation surface. Please either restrict the selector to a safe email/fingerprint-ish character set or build the command with real shell quoting (and add a regression for apostrophes/metacharacters).

  • Generated TOML accepts raw newlines/control chars and unquoted numeric fields. escape_toml only escapes backslash and ", while password/display-name/hosts/downloads-dir/GPG selector are interpolated into basic strings and --imap-port/--smtp-port are interpolated unquoted. A pasted secret or flag value with a newline can break the TOML or inject extra keys; non-numeric ports can also generate invalid/mutated TOML. This is local input, but it is the config writer for password-bearing files, so it should validate ports and reject/control-escape unsupported characters instead of writing surprising config.

I did not find a CI/template parity blocker, and the README is mostly honest about the new model. The caveat is that the README says “Himalaya config” generally; with the current awk/grep parser that is only safe for the generated top-level [accounts.name] + dotted-key shape, not arbitrary valid TOML.

Validation I ran: mise run test (92/92) on the PR branch.

@c0da-ricon

Copy link
Copy Markdown
Contributor Author

Thanks — agreed on all three blockers. Fixed in f4adf3a (fix: harden account config mutation).

What changed:

  • Account names are now restricted to simple unquoted TOML section names: letters, numbers, underscore, dash. Dotted names are rejected at setup.
  • email_account_names now only recognizes exact top-level generated account sections matching [accounts.<name>]; nested tables like [accounts.personal.backend] are ignored.
  • Account mutators (default, gpg enable, gpg disable, setup default-clearing) now only enter top-level generated account sections, so nested account tables do not become fake accounts and do not receive injected default/pgp.* keys.
  • GPG local-user selectors now reject quotes/whitespace/shell metacharacters via a conservative safe character set before writing pgp.sign-cmd.
  • Generated TOML string fields reject control characters/newlines before escaping.
  • IMAP/SMTP ports are validated as numeric and in range 1..65535.

New regressions cover:

  • nested account tables during account resolution;
  • nested account tables during account default mutation;
  • dotted account name rejection;
  • non-numeric port rejection;
  • newline/control rejection for password-bearing TOML output;
  • unsafe GPG local-user rejection in setup and account gpg enable.

Validation after the fix:

  • mise exec -- readme build
  • mise exec -- readme build --check
  • mise run test — 99/99
  • mise run test-integration — 35/35
  • bun test src/ — 44/44
  • git diff --check
  • codebase lint "$PWD" — 9/9
  • mise run doctor
  • git pre-commit
  • git pre-push

Could you re-review when you have a chance?

@c0da-ricon
c0da-ricon requested a review from ikma-ricon June 21, 2026 22:24
@c0da-ricon
c0da-ricon merged commit 9817427 into main Jun 22, 2026
2 checks passed
@c0da-ricon
c0da-ricon deleted the c0da/account-personas branch June 22, 2026 00:58
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.

3 participants