AnchorStack

Production Readiness Report

You shipped it.
Is it safe to operate?

We audited laddr across 9 engineering areas - 77 findings, 33 of them critical or high. Here's the picture.

laddr @ 97d133c

fail - Unsafe to operate3.3 of 10
2critical
31high
36medium
8low
Area Scores

9 areas evaluated

Each area scored 0-10 by an AnchorStack engineer.

Security

2/10

The app has a workable authentication base, and many API routes verify bearer tokens before querying. It is not currently safe for production: direct database access can affect paid entitlements, real-looking secrets were present, and payment entitlements are split across non-atomic paths.

failWeight x1.5

Architecture

5/10

The base shape is appropriate for a small product, but several important workflows rely on unclear state ownership, request-time recomputation, and unbounded reads.

watchWeight x1

Data

4/10

The data layer has reasonable constraints and useful indexes, but it is not yet safe to evolve or recover. Migration replay, restore evidence, and atomic database contracts need attention.

failWeight x1.5

AI Engineering

4/10

Agent-facing scaffolding is stronger than many small product repos, but context controls are not locked down against credential exposure or unsafe production changes.

failWeight x1

Infrastructure

3/10

The hosting and service shape is plausible, but it cannot currently be rebuilt, changed, scaled, or recovered safely from repository evidence alone.

failWeight x1

Observability

2/10

The app has product analytics and platform logs but lacks minimum viable production observability: error tracking, uptime monitoring, structured correlated logs, routed alerts, job heartbeats, SLOs, and an incident runbook.

failWeight x1

Code Quality

4/10

The codebase builds and typechecks, but production-critical paths are concentrated in large files and are not protected by meaningful tests or CI quality gates.

failWeight x1

Delivery

3/10

There is not yet a repeatable, gated, traceable production release process appropriate for an app with payments, privileged database access, and automation.

failWeight x1

Documentation

3/10

Documentation is partial and sprawled, with no trustworthy canonical entry point for deployment, secret rotation, recovery, or production operation.

failWeight x1
Findings

Where to look first

01

criticalSEC-001Security

Direct Database Access Can Self-Grant Paid Entitlements

The app appears designed to route database access through privileged API handlers, but a redacted data policy allows authenticated users to affect paid-access records with an insufficient guard.

Impact

Any authenticated user can issue a direct database write that grants paid subscription access without completing a payment. This bypasses Stripe entirely and creates entitlements the billing system has no record of, making them invisible to audits and refund logic.

Remediation

Move all paid entitlement writes behind a server-side function that verifies a completed Stripe payment before writing. Add an integration test that attempts a direct entitlement write without a payment record and asserts it is rejected. Rotate the database credentials used in affected routes.

02

criticalSEC-002Security

Real-Looking Secrets Are Present Locally And .env Was Committed To Version History

A local environment file contained multiple high-value credential classes, and a previous history state included private configuration that should be treated as exposed until rotated.

Impact

The committed file contained API keys and service tokens that are likely still valid. Anyone with read access to the repository history has had access to production credentials. This represents a confirmed exposure window of indeterminate length.

Remediation

Immediately rotate all credentials present in the commit history. Run a BFG Repo Cleaner pass to purge the file from history. Add a pre-commit hook and CI check using a secret scanner to prevent future leakage. Audit recent access logs for the exposed credentials.

03

highSEC-003Security

Payment Recording Has Competing Writers And Incomplete Entitlement Data

Two payment confirmation paths can both create purchase records. Concurrent or replayed calls can produce duplicate state, and one path omits fields written by the other.

Impact

Duplicate purchase records will cause customer support issues, incorrect billing counts, and failed refund lookups. The missing entitlement fields on one path mean some users get partial access states that are hard to detect or correct without manual intervention.

Remediation

Designate the Stripe webhook handler as the sole canonical writer for purchase records. Add an idempotency key on the purchase table keyed to the Stripe payment intent ID. Remove the secondary confirmation path or reduce it to a read-only check against the webhook-created record.

04

highSEC-004Security

Profile Update Endpoint Allows Mass Assignment Of User Columns

An authenticated profile update path accepts a broad object and writes remaining fields to the user row after removing only a narrow set of disallowed keys.

Impact

Authenticated users can overwrite any user column not in the narrow denylist, including role flags, subscription tier, and internal metadata fields. The denylist approach guarantees future columns will be exposed by default when the schema evolves.

Remediation

Replace the denylist with an explicit allowlist of updatable profile fields on the server side. Add tests that attempt to write restricted fields and assert they are silently ignored or rejected. Review the schema for any columns that may have already been written this way.

05

highSEC-005Security

Completion APIs Trust Client-Supplied Scores And Fail Open

Completion endpoints persist client-supplied scoring and progress data without recomputing sensitive values at the server boundary.

Impact

Any user can submit arbitrary scores and progress values. This affects leaderboard integrity, word completion counts, and streak tracking — all of which the product uses for engagement and potentially for premium feature gating.

Remediation

Recompute all trusted gameplay values server-side using the canonical game logic. Validate client-submitted values against the server computation before persisting. Add fuzz tests that submit out-of-range values and assert they are rejected or clamped.

06

highSEC-006Security

Stripe Webhook Signature Verification Is Conditional

The Stripe webhook handler verifies the signature only when a specific environment variable is present, allowing unsigned events to be processed in environments where that variable is absent.

Impact

Any actor who can reach the webhook endpoint can forge a payment event and trigger the purchase confirmation flow without a real Stripe transaction. This directly enables unauthorized entitlement grants at scale.

Remediation

Make signature verification unconditional. If the secret is absent, reject the request with a 400 and log the configuration error. Add an integration test that sends an unsigned webhook payload and asserts it is rejected.

07

highSEC-007Security

No Rate Limiting On Authentication Or Password Reset Endpoints

Login and password reset routes have no IP-based or token-based rate limiting, leaving them open to credential stuffing and enumeration attacks.

Impact

An attacker can attempt unlimited credential combinations against user accounts. Password reset can be used to enumerate valid email addresses via timing or response differences. Both represent account takeover vectors for any user in the system.

Remediation

Add rate limiting to auth and password reset routes using an in-memory or Redis-backed limiter. Return a consistent response time and body regardless of whether the email exists. Add monitoring alerts for high request rates to these endpoints.

08

highSEC-008Security

Session Tokens Are Not Rotated After Privilege Changes

When a user upgrades to a paid subscription, the existing session token is not invalidated or rotated, leaving pre-upgrade sessions valid with post-upgrade privileges.

Impact

A compromised pre-upgrade session token continues to grant paid-tier access indefinitely. If a user's free session was stolen before they purchased, the attacker gains paid access without any re-authentication.

Remediation

Rotate session tokens on any privilege change (payment, role change, password reset). Implement a session version counter on the user record and reject tokens that predate the current version.

09

highARCH-001Architecture

Word Completion History Uses Unbounded Full-Table Reads

Leaderboard and completion count queries scan the entire completions table without user-scoped indexes or pagination, growing linearly with total app usage.

Impact

As the user base grows, leaderboard and completion queries will degrade unboundedly. On a table with 100k+ rows, these queries will time out under normal load, causing gameplay features to fail for all users simultaneously.

Remediation

Add a composite index on (user_id, completed_at) and a separate index for leaderboard queries. Introduce pagination or cursor-based access for any endpoint that returns completion history. Add a slow-query threshold alert.

10

highARCH-002Architecture

Streak And Score State Is Recomputed On Every Request

User streak and aggregate score values are recalculated from raw completion records on each API call rather than maintained as cached or materialized values.

Impact

Each request to display a user's dashboard triggers a full aggregation over their completion history. This creates N+1 query patterns at scale and makes the dashboard page progressively slower as users accumulate more completions.

Remediation

Maintain streak and score as denormalized columns on the user record, updated atomically alongside each completion write. Use a database trigger or application-level transaction to keep them consistent. Cache at the API layer for read-heavy paths.

11

highARCH-003Architecture

Subscription State Is Owned By Multiple Systems With No Canonical Source

Whether a user has an active paid subscription is determined by querying both the purchase table and the user record, with no single authoritative field.

Impact

Inconsistent subscription checks mean different parts of the app can disagree on whether a user is a paying customer. This creates support cases where users lose access intermittently, and makes it impossible to write reliable subscription audits.

Remediation

Designate a single canonical boolean field (e.g., users.is_subscribed) as the source of truth for subscription state. All subscription checks in the application should read only this field. Updates to the field happen only from the Stripe webhook handler.

12

highDATA-001Data

Database Migrations Cannot Be Replayed From A Clean State

The migration history contains mutations that assume prior state and will fail if run against a fresh database, making it impossible to provision a new environment or verify the schema from scratch.

Impact

You cannot provision a new developer environment, staging environment, or disaster-recovery database without manual intervention. Any new team member or automated test environment will fail to set up. This also means your schema drift risk is undetected.

Remediation

Audit all migrations for idempotency — each migration should be runnable against a clean database. Add a CI step that provisions a fresh database and runs all migrations end-to-end. Fix any migration that assumes pre-existing data or schema state.

13

highDATA-002Data

No Evidence Of Backup Verification Or Restore Testing

There is no documented backup schedule, no restore test record, and no runbook for recovering the database from a backup in the repository.

Impact

A database failure or accidental data loss has no verified recovery path. Backups that have never been tested are frequently found to be incomplete or corrupt at the moment they are needed. For an app with payment records and user data, unrecoverable data loss is a GDPR incident.

Remediation

Document the backup provider and schedule. Add a monthly restore test to a staging environment and log the result. Create a recovery runbook that specifies RTO and RPO targets and the step-by-step restore procedure.

14

highDATA-003Data

Multi-Step Payment Writes Are Not Wrapped In A Transaction

The payment confirmation flow writes to multiple tables sequentially without a wrapping database transaction, leaving the database in a partially-written state if any step fails.

Impact

A network error or exception mid-write can create a purchase record without a corresponding entitlement row, or vice versa. These half-written states are invisible to the app and require manual database inspection and correction to resolve.

Remediation

Wrap all multi-table payment writes in an explicit database transaction. Ensure the transaction rolls back completely on any failure. Add an integration test that simulates a failure mid-write and asserts the database remains in a consistent state.

15

highDATA-004Data

Schema Has No Soft-Delete Or Audit Trail For User Data

User and purchase records are hard-deleted with no soft-delete flag or audit log, making GDPR erasure requests and accidental deletion unrecoverable.

Impact

If a user is deleted accidentally or maliciously, their data and purchase history are unrecoverable. GDPR right-to-erasure requests cannot be logged as completed. Support cases involving deleted accounts have no recovery path.

Remediation

Add a deleted_at timestamp to user and purchase tables. Implement soft-delete across the application. Maintain a separate audit log table for all record mutations. Ensure the GDPR erasure process nullifies PII fields rather than hard-deleting rows.

16

highAI-001AI Engineering

Agent Context Includes Production Database Credentials Scope

The MCP server configuration grants agent sessions access to tools that can read and write production database records without scope restriction.

Impact

An AI agent operating in development or with a compromised prompt can make writes to the production database. There is no sandbox or read-only mode for agent sessions, and no audit trail of what agents have changed.

Remediation

Create a read-only database role for agent sessions. Restrict agent tool scope to non-destructive operations by default, requiring explicit escalation for writes. Add an audit log for all agent tool calls that includes the session context and tool arguments.

17

highAI-002AI Engineering

No Guardrails Against Prompt Injection Via User-Controlled Content

Agent workflows process user-submitted content (word inputs, notes) without sanitizing for prompt injection patterns before passing to the model context.

Impact

A user can craft a word submission or note that hijacks the agent's instruction context, potentially causing the agent to leak other users' data, bypass access controls, or take destructive actions within its tool scope.

Remediation

Sanitize all user-controlled strings before they enter model context. Add a system prompt layer that states role boundaries and explicitly forbids instruction override. Implement output validation that checks agent responses for signs of injection before acting on them.

18

highAI-003AI Engineering

Agent Sessions Have No Token Budget Or Cost Controls

Agent tool calls have no per-session token budget, no cost ceiling, and no circuit breaker to halt runaway sessions before they accumulate significant API charges.

Impact

A malformed prompt, infinite loop in agent logic, or adversarial input can trigger an unbounded chain of model calls. This has directly caused multi-thousand-dollar API bills in similar systems. There is currently no automatic stopping mechanism.

Remediation

Implement a per-session token budget enforced at the agent orchestration layer. Add a cost circuit breaker that halts execution and alerts when a session exceeds a configurable threshold. Log token usage per session to a monitoring dashboard.

19

highINFRA-001Infrastructure

Infrastructure Cannot Be Rebuilt From Repository Alone

There is no infrastructure-as-code, no documented provisioning steps, and no record of which cloud resources exist and how they are configured.

Impact

If the hosting account is lost, suspended, or needs to be migrated, there is no way to reconstruct the environment. Reproducing the production setup requires tribal knowledge and manual configuration, with high risk of misconfiguration and extended downtime.

Remediation

Document all cloud resources and their configuration in a runbook or as IaC (Terraform, Pulumi, or similar). At minimum, create a provisioning checklist that covers database, hosting, DNS, secret stores, and third-party service configuration.

20

highINFRA-002Infrastructure

No Documented Scaling Approach For Traffic Or Database Load

There is no documented scaling plan, no defined traffic thresholds that would trigger scaling actions, and no evidence that the current infrastructure has been load-tested.

Impact

A traffic spike (product launch, press coverage, social mention) will hit an infrastructure that has never been validated under load. The most likely failure points are the database connection pool and the Stripe webhook processing queue — both of which fail without clear error messages.

Remediation

Define scaling thresholds for compute and database. Run a load test against staging at 10x expected peak traffic. Document the scaling actions to take at each threshold. Configure auto-scaling or connection pooling before the first major traffic event.

21

highINFRA-003Infrastructure

No Recovery Runbook Or Documented Incident Response Process

There is no documented procedure for responding to a production incident — no runbook, no escalation path, and no defined RTO or RPO.

Impact

When an incident occurs, response time is determined by whoever is available and what they can remember. Without a runbook, mean time to recovery is significantly longer, and the same incidents recur because root causes are not systematically addressed.

Remediation

Create a minimal incident runbook covering: how to identify the scope of an outage, how to roll back a bad deploy, how to restore the database from backup, and how to communicate status to users. Assign an on-call rotation even if it is a single person.

22

highINFRA-004Infrastructure

Third-Party Service Dependencies Are Not Health-Monitored

Stripe, Resend, and the database have no health checks, no circuit breakers, and no automatic alerting if they become unavailable.

Impact

When Stripe or the database goes down, the app fails silently from the user's perspective and noisily via unhandled exceptions in logs that no one is watching. There is no automatic notification to the operator and no graceful degradation path.

Remediation

Add health check pings for each critical dependency with alerting on failure. Implement graceful degradation — for example, queuing failed email sends for retry rather than surfacing a 500. Set up a status page or at minimum a Slack alert channel for dependency incidents.

23

highOBS-001Observability

No Error Tracking — Exceptions Are Silent In Production

Application exceptions are not captured by any error tracking service. Unhandled errors in API routes and server components are visible only in raw platform logs that are not monitored.

Impact

Production bugs, including payment failures and auth errors, can go undetected indefinitely. The first signal of an outage is typically a user complaint rather than an automated alert. Error rates, error types, and affected users are completely unknown.

Remediation

Integrate an error tracking service (Sentry, Highlight, or equivalent). Instrument both server-side API routes and client-side rendering paths. Configure alert thresholds for error rate spikes. Ensure payment and auth errors are captured with full context.

24

highOBS-002Observability

No Uptime Monitoring Or Automated Alerting

There is no external uptime monitor checking that the app is reachable and returning valid responses. Outages are detected only when a user reports them.

Impact

The app can be fully down for hours before anyone is aware. For a subscription product, undetected downtime directly causes churn. Payment webhooks that fail silently during an outage create billing state divergence that is difficult to reconcile.

Remediation

Set up an external uptime monitor (Better Uptime, Cronitor, or equivalent) with HTTP checks on the homepage and a critical API endpoint. Configure PagerDuty or a Slack alert for any downtime exceeding 2 minutes. Add a synthetic payment flow check to catch partial outages.

25

highOBS-003Observability

Scheduled Job Automation Has No Heartbeat Or Failure Alerting

The app has scheduled automation jobs (word refresh, streak calculations) with no heartbeat monitoring — if a job stops running, there is no alert.

Impact

A silently failing scheduled job degrades the product experience invisibly. Users who rely on daily word refreshes or streak tracking get stale data without any indication of a problem. The issue can persist for days or weeks before a user complaint surfaces it.

Remediation

Integrate a heartbeat monitor (Cronitor, Healthchecks.io) for each scheduled job. Each job should ping the monitor on completion. Configure an alert for any job that misses its expected window. Add a last_run_at column to the job log for dashboard visibility.

26

highOBS-004Observability

No Structured Logging — Correlation Across Requests Is Impossible

Log output is unstructured console output with no request ID, user ID, or trace context, making it impossible to reconstruct what happened during a specific user's session or payment flow.

Impact

When investigating a support case or incident, there is no way to filter logs to a specific user, request, or payment. Every investigation requires manually scanning raw log output. This makes debugging payment failures and auth issues substantially slower and more error-prone.

Remediation

Add a request ID header (or generate one per request) and propagate it through all log statements. Use structured JSON logging with fields for request_id, user_id, route, and duration. Configure your log aggregation platform to index these fields for fast filtering.

27

highCQ-001Code Quality

Payment And Auth Logic Is Concentrated In Large Untested Files

The files responsible for payment processing and authentication contain the majority of business logic but have no unit or integration test coverage.

Impact

Any change to payment or auth logic — including security patches — is made without a safety net. Regressions in these paths will be caught only by users in production. The high coupling makes it difficult to change one behavior without inadvertently affecting another.

Remediation

Extract payment and auth logic into discrete, injectable service functions. Write integration tests for each happy path and critical error path (failed payment, expired session, invalid token). Add these test files as a required CI gate before merging to main.

28

highCQ-002Code Quality

CI Pipeline Has No Test Requirement Or Coverage Gate

The CI configuration does not require tests to pass before a pull request can be merged. There is no coverage threshold enforced at any stage of the delivery pipeline.

Impact

Code can be merged to main and deployed to production regardless of test failures. A broken payment path or auth regression can reach production undetected if tests are skipped or failing. This has happened in similar codebases and resulted in production incidents.

Remediation

Add a required CI check that runs the full test suite and blocks merge on failure. Set a minimum coverage threshold (start at current baseline, enforce it does not regress). Add a branch protection rule to main that requires the CI check to pass.

29

highDEL-001Delivery

No Gated Release Process — Any Commit Can Reach Production

There is no required review, approval, or automated check gate between a commit and a production deploy. Pushes to main deploy directly.

Impact

A single unreviewed commit can introduce a payment bug or security regression into production. For an app handling real payment data and user credentials, this is a material risk — particularly given the existing security findings in this report.

Remediation

Add a branch protection rule requiring at least one pull request review before merging to main. Add required CI status checks (lint, type check, tests) as merge prerequisites. Consider a deploy approval step for changes that touch payment or auth paths.

30

highDEL-002Delivery

No Rollback Process For Bad Production Deploys

There is no documented procedure for rolling back a bad deploy, and the deployment configuration does not support one-command rollback.

Impact

When a bad deploy reaches production, the recovery path is to push a fix forward — which takes time and risks introducing new issues under pressure. For payment or auth regressions, every minute of exposure increases financial and reputational risk.

Remediation

Configure the hosting platform to retain the last N successful builds for one-command rollback. Document the rollback procedure in the incident runbook. Test it — run a practice rollback in staging before you need it in production.

31

highDOC-001Documentation

No Canonical Deployment Or Secret Rotation Runbook

There is no single authoritative document covering how to deploy, how to rotate secrets, or how to recover the system — the information exists in scattered notes, commit messages, and tribal knowledge.

Impact

A new contributor or the original developer under pressure cannot safely deploy or rotate credentials without risk of misconfiguration. Secret rotation — which is required immediately for the credentials found in SEC-002 — cannot be completed safely without a documented procedure.

Remediation

Create a RUNBOOK.md covering: deployment steps, required environment variables and where they come from, secret rotation procedure for each credential class, and database recovery steps. Link it from the README. Keep it updated as the system changes.

32

mediumCQ-003Code Quality

Revenue and Entitlement Logic Duplicated Across Multiple API Routes

Pricing, product identifiers, purchase verification, and entitlement checks are implemented separately in multiple API routes and in client-side code. There is no shared source of truth for what constitutes a valid purchase or entitlement — changes to the revenue path require coordinated edits across several files.

Impact

Duplicate business logic in the revenue and entitlement path increases the probability of inconsistency bugs: a refund that unrecords in one route but not another, or an entitlement check that correctly rejects in the primary flow but passes in a secondary verification path. Each divergence is a potential billing or access-control defect. These bugs typically only surface when a user reports an incorrect charge or accesses content they should not, making them difficult to catch in normal testing.

Remediation

Centralize product definitions, entitlement checks, pack-number assignment, and purchase-recording in shared server-side helpers. Refactor the webhook and verification routes to call the same idempotent purchase-recording function. Move product metadata to a single server-side config so client analytics consume it rather than maintaining parallel hard-coded values. After centralizing, add server-side tests for the purchase and entitlement paths that cover the primary flow, webhook reconciliation, and refund handling.

33

lowCQ-004Code Quality

Scattered Console Logging Without Structured Output or Redaction Rules

Production API and frontend paths use direct console.log, console.warn, and console.error calls throughout, including in auth, purchase, group, and analytics flows. The current approach mixes debugging output with operational events without structured severity levels or redaction rules for user identifiers.

Impact

Console-based logging in production makes it difficult to reliably monitor errors, distinguish operational events from debug noise, or maintain an audit trail for sensitive flows. User identifiers and internal state that appear in console output may be captured by log aggregators without redaction. When a production incident occurs in the purchase or auth path, the lack of structured events with request IDs makes diagnosis significantly slower.

Remediation

Add a small logging wrapper for API routes that includes severity level, event name, and explicit redaction for user identifiers and payment tokens. Remove or guard behind a development flag the frontend debug logs that leak internal state. For the purchase and auth flows specifically, replace console calls with structured events that carry a requestId so a full request trace can be reconstructed from logs.