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
18 lines
555 B
Markdown
18 lines
555 B
Markdown
# openmail.karti.ai
|
|
|
|
Static single page. No build step, no framework, no JS — it is a document, and
|
|
a document that needs a bundler is a liability.
|
|
|
|
## Deploy
|
|
|
|
Served by Caddy on cloud-2 from `/var/www/openmail`.
|
|
|
|
```bash
|
|
rsync -av --delete website/ cloud-2:/var/www/openmail/
|
|
```
|
|
|
|
⚠️ **The cloud-2 Caddy trap:** a site block without an explicit
|
|
`bind 10.0.0.2` serves a *valid certificate and an empty 200*. It looks
|
|
deployed and returns nothing. Always include the bind, and always verify with
|
|
`curl -sI https://openmail.karti.ai` — not by eye.
|