Commit Graph
6 Commits
Author SHA1 Message Date
Karti Tripathi bb8f32ee61 CI: set PATH in the workflow; add a toolchain diagnostic step 2026-09-02 13:55:35 -07:00
Karti TripathiandClaude Opus 5 7fba0ab0c3 CI: run every job on spark-1; drop rust-cache
Two failures, two causes.

GitHub-hosted jobs never started: 'recent account payments have failed or
your spending limit needs to be increased'. Not a config problem. Public
repos get hosted runners free, so this may resolve itself; meanwhile every
job runs on spark-1, which is also the deployment architecture (Oracle
Ampere A1 is aarch64), so this is an improvement rather than a workaround.

The aarch64 job failed with exit 127 — cargo not found — and kept failing
after each fix. Cause: Swatinem/rust-cache's post-step prunes ~/.cargo.
That is correct on a GitHub-hosted runner where ~/.cargo is disposable, and
destructive on a self-hosted one where it is the real toolchain. It deleted
~/.cargo/bin/rustup after the first successful run, leaving every shim a
dangling symlink. The action is removed and the toolchain moved to
/opt/rust, wired through the runner's .env and .path, so it is out of reach
even if someone re-adds it. Caching bought nothing here anyway — the runner
keeps its target dir between runs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JkyvfNJGTshJNE9FtwPLk7
2026-09-02 13:53:50 -07:00
Karti TripathiandClaude Opus 5 037e87bc60 Wire CI: match runner label case, commit Cargo.lock
The aarch64 job targets the spark-1 self-hosted runner, whose labels are
registered as 'Linux' not 'linux'. Cargo.lock is committed because the
Dockerfile builds with --locked and a lockfile-free reproducible build is
not reproducible.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JkyvfNJGTshJNE9FtwPLk7
2026-09-02 13:41:01 -07:00
Karti TripathiandClaude Opus 5 f0ec4ad691 Add ROADMAP; settle ADR 0005 with a measured OCI port test
cloud-1 (phx) and cloud-2 (us-sanjose-1) are both already OCI aarch64
instances, so the open question in ADR 0005 was answerable directly rather
than by reading forum posts. Measured from cloud-2:

  BLOCKED  25    gmail-smtp-in.l.google.com, aspmx.l.google.com
  OPEN     587/465/2525  gmail, sendgrid, SES, OCI Email Delivery

The community reports claiming 587 and 2525 are also blocked are wrong.
Only 25 is blocked, exactly as Oracle documents, so relay works and the
launch host is viable. Inbound :25 reachability is still unverified — that
is a VCN security-list change, not a platform limit.

ROADMAP.md carries the goal, three success signals that are deliberately
not v0.1, a definition-of-ready checklist, and five standing rules.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JkyvfNJGTshJNE9FtwPLk7
2026-09-02 13:38:01 -07:00
Karti TripathiandClaude Opus 5 a42b798a0e Scaffold tier 2 and 3, the schema, the website, and self-hosted-first
Store: the full Postgres schema as an embedded migration. UUIDv7 keys so
`ORDER BY id` is a free chronological index; raw MIME and attachments live
in object storage with only a key in the row; `pods` present from day one
because retrofitting tenancy costs more than an unused column. API keys are
stored as a SHA-256 hash — a database dump must not be a set of live
credentials.

API: the v0 route table, including `ingest`, which closes the
receive→thread→extract loop with zero mail infrastructure and is what makes
the agent layer testable in CI. Scopes are a closed enum rather than
strings, so "can send mail" and "can mint keys" are not one typo apart.
Internal errors are logged in full and reported as a bare string.

MCP: the tool catalogue, six tools. Adding a row here is the only way an
agent gains a capability — a new REST route is invisible until someone opts
it in. Three tests guard the rule that no tool can ever reach key
management; CI fails rather than production.

ADR 0006: enterprise self-hosted first. A hosted offering comes only after
we have run this ourselves long enough to have a deliverability record
worth selling. `pods` stays in the schema as the thing that keeps that path
open — do not remove it as dead code.

Also: multi-stage Dockerfile running as a non-root system user with no
shell in the runtime image, and the openmail.karti.ai static page.

17 tests, zero clippy warnings, fmt clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JkyvfNJGTshJNE9FtwPLk7
2026-09-02 13:20:51 -07:00
Karti TripathiandClaude Opus 5 36b15ddcaf Rewrite as a Rust, Apache-2.0 workspace
Supersedes the Go + embed-Mox design. The Go tree is removed; its
architecture doc is preserved at docs/archive/ARCHITECTURE-go-embed-mox.md
because its competitive analysis and data model still hold.

Five decisions recorded as ADRs:

  0001  Rust, not Go — accepting ~5,500 lines of protocol code that Mox
        would have given us free, to get the first permissively licensed
        Rust mail server. Costs stated plainly.
  0002  Apache-2.0, not MIT or AGPL — patent grant, trademark, CLA-free
        contribution. Public on GitHub; Gitea stays as the private fallback.
  0003  Stalwart's primitive crates (Apache-2.0/MIT) yes; its AGPL server
        crates never. DANE and MTA-STS sit on the AGPL side of that line,
        which is why we write our own.
  0004  Milestones, reordered: embedded inbound is required at launch.
  0005  Oracle Cloud blocks outbound :25, so direct-to-MX is impossible on
        the launch host. Split delivery is mandatory, not an on-ramp.

Twelve crates in three tiers. Tier 1 (mail-dane, mail-mta-sts, mail-dsn)
is standalone and publishable — no `dane` or `mta-sts` crate exists on
crates.io at all today.

openmail-relay ships the provider table as data, with SES and Oracle from
the start. Oracle's and Resend's SPF includes are deliberately None: a
guessed include turns the DNS check green against a mechanism the provider
does not honour, and mail still fails SPF silently.

cargo check/test/clippy/fmt all green; unsafe_code is forbidden workspace
wide; cargo-deny enforces the licence policy in CI.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JkyvfNJGTshJNE9FtwPLk7
2026-09-02 13:08:05 -07:00