Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,20 @@ Shipped migrations are immutable. Each ledger row carries a checksum of the migr
statements, so editing one that has already been applied fails loudly on the next boot
rather than letting fresh and existing databases diverge. Add a new migration instead.

`schema.ts` and `migrations.ts` must agree statement for statement — the drizzle table
object is a public export, and `src/schema-ddl-parity.test.ts` diffs the two against a
live database. Change one, change the other, in the same commit.
`schema.ts` and `migrations.ts` must agree statement for statement — the runtime
queries read through the drizzle table object, and `src/schema-ddl-parity.test.ts`
diffs the two against a live database. Change one, change the other, in the same commit.

## Agent-originated mail

`createMailboxPersist` wraps the host's own persist function. `authorizeSender` is the
host's check that a sender address is one it recognizes right now: a hub answers by
looking up the tenant a mailbox-routable address (a person, or a live agent run)
currently resolves to, and refuses anything else. `upstream` is the host's existing
mail-persist path. The wrapper calls it unconditionally and layers the durable inbox
write on top, so a transport failure never costs a recipient the copy that makes the
message readable later. The host calls the wrapped function wherever it delegates an
outbound frame; it does both writes.

## Pull requests

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export function wrapPersistMail<R>(
}
```

`authorizeSender` is the host's own check that a sender address is one it recognizes right now — a hub answers by looking up the tenant a mailbox-routable address (a person, or a live agent run) currently resolves to, and refusing anything else. `upstream` is the host's own pre-existing mail-persist path — the write it already made before this package existed; `createMailboxPersist` calls it unconditionally and layers the durable inbox write on top, so a transport failure never costs a recipient the copy that makes the message readable later. Call the wrapped `persistMail` wherever the host currently delegates an outbound frame; it does both writes.
`authorizeSender` says whether a sender address is one the host recognizes right now, and which tenant it belongs to. `upstream` is the host's existing persist path; it always runs. See [CONTRIBUTING.md](./CONTRIBUTING.md#agent-originated-mail) for how the two writes interact.

## How it works

Expand Down
7 changes: 2 additions & 5 deletions src/frame.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ function headers(raw: Uint8Array) {
}

describe("buildMailFrame headers", () => {
// Nothing used to assert a single header value this function produces,
// which is how a doubly-bracketed Message-ID shipped unnoticed.
test("emits every header it promises, verbatim", () => {
const h = headers(frame());
expect(h.get("from")).toBe("bot@example.com");
Expand All @@ -45,9 +43,8 @@ describe("buildMailFrame headers", () => {
});

test("a Message-ID from @intx/mime survives the frame unchanged", () => {
// Regression: generateMessageId already returns `<uuid@domain>`. The frame
// builder used to wrap it a second time, writing `<<uuid@domain>>` into
// every `raw` — frozen and corrupt at rest, and unthreadable by any MTA.
// generateMessageId already returns `<uuid@domain>`; wrapping it again
// would write `<<uuid@domain>>`, which no MTA can thread.
const generated = generateMessageId("bot@example.com");
expect(generated).toMatch(MESSAGE_ID);
expect(headers(frame({ messageId: generated })).get("message-id")).toBe(
Expand Down
2 changes: 1 addition & 1 deletion src/migrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ export const MIGRATIONS: Migration[] = [
//
// uid/modseq become NOT NULL: every remaining write path is
// `NativeMailboxStore.append`, which always sets both. The backfill below
// is defense in depth for a row inserted by the pre-cutover write paths
// is defense in depth for a row inserted by a non-native write path
// between `0004` running and this migration — same per-(tenant,
// principal, folder) row_number() `0004` used, guarded by "uid" IS NULL
// so an already-backfilled row is left alone.
Expand Down
5 changes: 1 addition & 4 deletions src/mount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ const DEFAULT_HEARTBEAT_INTERVAL_MS = 25_000;

/**
* Ceiling on SSE events queued for one connection whose client has stopped
* reading — see the identical rationale this carried before the native-store
* cutover: an event is a nudge, never the data, so a stalled consumer is
* reading. An event is a nudge, never the data, so a stalled consumer is
* disconnected rather than buffered for.
*/
export const MAX_PENDING_SSE_EVENTS = 100;
Expand Down Expand Up @@ -144,8 +143,6 @@ type MailboxListItem = {
raw: string;
};

// The five single-message mutations that move or flag a message. `op` is the
// event op published on success.
/**
* One node of `GET /me/inbox/threads(/:rootUid)`: `@intx/mailbox`'s
* `executeThread`'s ref (recursively, as `children`) plus the same envelope
Expand Down
Loading