Skip to content

Codebase audit

This is a contributor-facing risk register produced while validating the documentation against the entire repository on 2026-07-23. It records observed behavior, not a claim that every item is exploitable in production. None of the code risks below was silently changed as part of the documentation rewrite.

The architecture is coherent and unusually well-commented for its size: provider adapters share a small contract, tombstones make offline deletion reliable, secrets are encrypted, request IDs cross subsystem boundaries, and backup/restore scripts validate their artifacts.

The calendar-detail P0, database-integrity, quality-gate, single-replica contract, exact-account OAuth cleanup, and federation token-lifecycle findings are resolved, as is release/toolchain reproducibility. The largest remaining risks are at boundaries:

  1. provider event delivery is best-effort without a durable retry queue;
  2. first-contact federation home-server claims are still unverified metadata.
Priority Meaning
P0 Address before treating the current API as hardened for untrusted multi-user deployment
P1 High-value reliability/delivery work; plan explicitly
P2 Important hardening or maintainability work
P3 Cleanup, consistency, and developer-experience debt

Status: resolved. getCalendarDetailsForUser() now checks the caller’s calendar role before loading either the calendar or its members. The calendars.test.ts regression self-check proves a non-member receives Forbidden and neither protected database read runs.

Originally observed: GET /api/v1/calendars/:id used requireAuth, but handlerGetCalendar read the calendar and its members without verifying that req.user belongs to it. The returned member shape includes names and email addresses.

Original impact: an authenticated user who learned a calendar UUID could read metadata and member contact information for a calendar they did not belong to.

Source: apps/api/src/index.ts and apps/api/src/handlers/calendars.ts.

Remaining follow-up: expand the same non-member coverage into a route-level authorization matrix for every endpoint that accepts a resource ID.

Status: resolved for local database writes. The query layer now commits each of these invariants in one Drizzle transaction:

  • a calendar row and its owner membership;
  • an event, its creator-attendee row, and all initial calendar links;
  • an event edit, link reconciliation, and removed provider mappings;
  • unlinking an event and tombstoning it when the final link disappears;
  • calendar deletion, origin tombstones, cascaded links, and orphan cleanup; and
  • external-mirror disablement or whole-account disconnect plus all local mirror cleanup (including CalDAV credentials).

Ownership transfer also locks the calendar row before changing both member roles and creatorID, so concurrent transfers cannot both proceed from the same owner snapshot. Provider mirrors reject ownership transfer because their credentials remain scoped to the original connected account.

Originally observed: these writes ran as independent statements. A database error or process exit between them could leave ownerless calendars, partially linked events, stale mappings, or roles that disagreed with creatorID.

Provider boundary: a PostgreSQL transaction cannot roll back Google, Microsoft, or CalDAV. Calendar writes retain their documented provider-first ordering, while event delivery remains best-effort. Durable recovery for that cross-system boundary is tracked separately under make provider event delivery recoverable.

P1 — add database constraints for assumed uniqueness

Section titled “P1 — add database constraints for assumed uniqueness”

Status: resolved. Migration 0033_busy_thunderball.sql removes historical duplicates before adding:

  • a primary key on user_settings.id; and
  • a unique constraint on calendar_events(event_id, calendar_id).

Settings creation now absorbs a concurrent insert, settings saves use one upsert, and event-link inserts use ON CONFLICT DO NOTHING. The schema self-check asserts both constraints are present in Drizzle metadata.

Originally observed: neither invariant was enforced by PostgreSQL. Lazy settings creation and event-link retries could create duplicate rows, leaving reads and updates ambiguous or multiplied.

Remaining follow-up: exercise the migration and concurrent writes against disposable PostgreSQL in CI as part of provider and database integration coverage.

P1 — make provider event delivery recoverable

Section titled “P1 — make provider event delivery recoverable”

Observed: event create/update/delete persists locally, then pushEventToCalendars catches and logs individual provider failures. The API can therefore succeed while Google, Microsoft, or CalDAV remains stale. There is no durable retry queue. Calendar-level operations generally use provider-first semantics, so the two write classes behave differently.

Impact: temporary provider outages create divergence that may not automatically reconcile in the expected direction.

Recommendation: persist an idempotent outbox job in the same transaction as the local event change, retry with backoff, expose pending/failed delivery state, and document conflict resolution. Until then, alert on stage="push" and confirm convergence manually.

P1 — restore an enforceable quality gate

Section titled “P1 — restore an enforceable quality gate”

Status: remediated on 2026-07-23.

Observed on 2026-07-23:

  • there is no pull-request CI workflow;
  • the Docker release workflow builds/publishes images without a preceding repository check;
  • root build exercises only workspaces that declare a build script;
  • the existing tests are twelve direct tsx assertion programs with no runner, coverage, database integration, or authorization matrix; and
  • client lint reports 82 errors and 55 warnings.

Resolution: the genuine lint errors and ordinary hygiene warnings were fixed. React Compiler diagnostics that currently reject Reanimated mutable shared values, gesture live refs, and legacy state-reset effects remain visible as warnings. expo lint --max-warnings 108 locks that debt so it can only move down, and pnpm check now includes lint.

.github/workflows/quality.yml runs a frozen install, typechecks, the twelve self-checks, lint, docs build, Drizzle generation drift detection, and a production API Docker build for every pull request and push to main. The manual Docker publication workflow calls the same reusable gate before it can push an image or create a release.

Follow-up: migrate Reanimated .value access to compiler-compatible get()/set() where supported, resolve effect dependency findings, and lower the warning budget with every cleanup. Expand the twelve assertion programs into route/database integration coverage as tracked below.

P1 — make horizontal scaling explicit or coordinated

Section titled “P1 — make horizontal scaling explicit or coordinated”

Status: remediated as an enforced single-replica contract on 2026-07-23.

Observed: scheduled provider sync, cleanup timers, SSE clients, rate limits, and metrics are process-local. Each API replica starts the timers and sees only its own connections/requests.

Impact: multiple replicas can duplicate scheduled work, miss cross-replica SSE broadcasts, and apply per-instance rather than global rate limits.

Resolution: the API now holds a dedicated PostgreSQL session advisory lock and refuses to start a second process against the same database. Both Compose files explicitly declare one API replica. Cleanup and external sync use a tested single-flight wrapper; an overlong run skips the next tick and emits a structured warning plus musubi_scheduled_task_skips_total{task}. The shipped Prometheus rules alert on any skip.

Follow-up: horizontal scaling remains intentionally unsupported. Replacing the singleton contract still requires job leadership/distributed locking, shared pub/sub for SSE, a shared rate limiter, and per-instance metric aggregation.

P1 — disconnect exactly one OAuth account

Section titled “P1 — disconnect exactly one OAuth account”

Status: remediated on 2026-07-23.

Observed: the connection handler asks Better Auth to unlink one account. If that fails, its fallback calls cleanUsersOAuthTokens(userID, provider), which is provider-wide rather than account-specific.

Impact: disconnecting one of several Google or Microsoft accounts can clear tokens for sibling accounts in the fallback path.

Resolution: the modern disconnect fallback now calls cleanOAuthAccountTokens(userID, provider, accountID), whose update predicate contains all three identifiers. Unsupported providers are rejected before any cleanup. A disposable-PostgreSQL integration test proves the target row is cleared while a sibling account for the same provider, another provider, and the same account ID owned by another user remain unchanged.

The old Google revoke endpoint has no account ID and remains explicitly provider-wide for backward compatibility; its helper is named cleanProviderOAuthTokens so it cannot be mistaken for exact-account cleanup.

P2 — normalize malformed-input responses

Section titled “P2 — normalize malformed-input responses”

Status: remediated for the observed event paths on 2026-07-23.

Observed:

  • event delete parses EventSchema outside the handler’s validation catch; and
  • GET /events?since= constructs a Date without rejecting an invalid value.

Impact: malformed client input may surface as 500 instead of a stable 400, polluting error alerts and hiding the actual caller defect.

Resolution: event create/update/delete share one body parser that converts shape failures to BadRequestError and validates every event/calendar UUID before a query. Event route parameters use the same UUID guard. GET /events?since= now rejects empty, invalid, array, and object values before calling the database. The global error mapper also treats uncaught ZodError as 400 rather than 500.

A direct self-check covers malformed UUIDs, timestamps and Zod bodies plus the error mapper’s 400/500 boundary.

Follow-up: extend the same route-boundary matrix to calendars, invites, federation and user routes, including invalid enum and cursor cases.

P2 — review federation identity and token lifecycle

Section titled “P2 — review federation identity and token lifecycle”

Status: remediated on 2026-07-23, with the first-contact trust boundary documented explicitly.

Observed: invite acceptance trusts the remote profile supplied by the bearer of the invite; it does not prove control of the claimed home server. Member tokens have no expiry, and the exported revocation helper is not part of a clear lifecycle. Kicking a member removes calendar authorization, but stale authentication tokens can remain stored.

The public invite-preview endpoint also returns live calendar context, including events and member email data, to anyone possessing a valid invite token. That may be intended capability behavior, but it is a meaningful privacy surface.

Recommendation: document the trust model, minimize preview data, add expiry and revocation/rotation, and authenticate remote identity if it is used for more than display. Include invite leakage and replay in threat modeling.

Resolution: unverified homeServer + email claims are no longer used to find an existing shadow user. First contact always creates an isolated identity; reusing a shadow across later invites requires its current member token as proof. This closes the cross-calendar privilege escalation where a holder of one invite could claim another shadow’s profile and inherit all of its memberships.

Member tokens now expire after 90 days. Versioned token values carry a client-readable issue time, the client rotates in the final 14 days, and the origin performs compare-and-swap rotation so one credential cannot be exchanged twice. Expired hashes are purged hourly. Removing a shadow’s final membership revokes its tokens in the same database transaction.

The invite preview now returns only calendar identity, member names/avatars, and minimal event fields for the 30-day UI window. It omits member email, event description, location, URL, attribution and unrelated historical one-off events.

A pure security self-check covers token format/timing, origin validation and preview minimization. A disposable-PostgreSQL integration test covers expiry, single-use rotation, unproved profile isolation, proved identity reuse and last-membership revocation.

Remaining boundary: v1 still does not prove that the first-contact person controls the claimed home server. Those claims are display metadata and cannot bind to existing access. Add a signed or callback-based server-to-server assertion before treating them as globally verified identity.

P2 — improve provider and database integration coverage

Section titled “P2 — improve provider and database integration coverage”

Status: provider coverage expanded on 2026-07-23; route/offline coverage remains open.

Remaining coverage gaps include:

  • PostgreSQL constraints, migrations, transactions, or concurrent writes;
  • route authentication/authorization beyond the calendar-detail regression and error mapping;
  • signed/callback-based federation identity proof and live transport failures; or
  • offline-client behavior beyond the tombstone retention window.

Use disposable PostgreSQL in CI, fake provider servers at adapter boundaries, and a small route-level test matrix. Keep live sandbox smoke tests as a separate release check.

Resolution so far: always-on self-checks now run local fake Google and Microsoft Graph HTTP servers. They cover pagination, partial-page discard after 410, full-set reset cursors, Graph series-master cache reset/reuse and a safe retry after a transient provider response. The new reset assertion exposed and fixed a stale-master cache surviving from the discarded delta pages.

Provider/account orchestration is isolated in a dependency-free boundary test: one discovery/account failure does not block healthy siblings, retryable accounts run again in the next cycle, strict connect flows still throw, and provider/account filters stay scoped.

pnpm test:provider-db adds a disposable-PostgreSQL OAuth lifecycle test with a fake token endpoint. It proves encrypted access/refresh-token rotation, account-scoped invalid_grant reconnect state, sibling preservation, and a transient 503 remaining active and succeeding on retry.

Remaining: make disposable PostgreSQL mandatory in CI; add the complete route authentication/authorization/error matrix; cover signed federation identity proof and live transport failures; and define/test client behavior beyond the tombstone retention window. Live provider credentials remain a separate release smoke test.

Resolved P3 — align versions and build reproducibility

Section titled “Resolved P3 — align versions and build reproducibility”

Observed before remediation:

  • root pins [email protected], while some package metadata/Dockerfiles still declare pnpm 10; the API image build prepares pnpm 10 but root metadata still makes Corepack download pnpm 11.8.0 during pnpm install;
  • root, workspace, docs, and native app versions do not express one clear release version;
  • the docs image installs an unpinned global pnpm and the full workspace without a frozen lockfile;
  • API image installation also relaxes lockfile reproducibility; and
  • the iOS forced-update URL still contains an <APP_ID> placeholder.

Resolved on 2026-07-23: root package.json is now the only source for the product version and exact pnpm version. Expo reads that product version directly; private workspace manifests no longer advertise unrelated versions or toolchains. Both images use Corepack, filtered production installs, frozen lockfiles, and the same digest-pinned Node base. pnpm release:verify protects these invariants, the Docker release job requires its requested version to match the manifest, and production EAS config rejects a missing/malformed iOS store URL.

  • Provider-neutral sync contract with provider-specific behavior isolated in adapters.
  • Soft tombstones and client delta reconciliation for offline correctness.
  • AES-GCM encryption for stored CalDAV credentials and roaming member tokens; only member-token hashes authenticate at origin.
  • Registered-route metric labels and structured request correlation.
  • Provider cursor reset/full-set sweep semantics are documented in code.
  • Backup scripts use restrictive permissions, validate partial dumps, and keep restore isolated.
  • Source comments explain several load-bearing client state and recurrence decisions.
  1. Completed: protect calendar-detail reads and add a non-member regression self-check.
  2. Completed: make core writes transactional and enforce the remaining uniqueness constraints.
  3. Completed: establish PR CI and make lint green/enforceable.
  4. Add a durable provider outbox plus visible delivery health.
  5. Completed: enforce and document the supported single-replica deployment.
  6. Completed: scope modern OAuth disconnect cleanup to one account.
  7. Partially completed: isolate unproved federation identities, add token expiry/rotation/revocation, and minimize previews; first-contact server proof remains.
  8. Completed: consolidate toolchain, image, and release versioning. Keep pnpm release:verify in the required release path.