Field notes from a market-intelligence startup

Making an AI-Assisted Platform Commercial Grade

Two founders write most of the code with AI coding agents, and the platform takes payments and holds member data. These notes catalog the controls that make it safe.

Headline number
120+ blocking checks on every pull request
Role
Architected and led
Category
Field Notes
Updated

Architecture

  1. Founders + AI agentspropose a change and stop
  2. Repo rules + hooksprod exec blocked, tested
  3. Pull-request gate120+ self-testing checks
  4. Deploy pathOIDC on main, migrate first, smoke
  5. Private estateone way in, one way out, 12 identities
    Append-only data layertriggers, versioned rows, PITR
    Perimeter + OAuth 2.1default-deny API, hashed tokens
  6. Detection + feed healthdelete-locked rules, heartbeat
    Paying membersmeasured numbers only
Every change, whoever wrote it, passes the same layers: repository rules and hooks, a self-testing pull-request gate, a pinned deploy path, and a private estate whose data layer, perimeter, and connector are watched by detection and feed health.

Key takeaways

  • Nothing depends on trusting the author of a change, human or agent. Every rule on this page is enforced by a machine, and the guards prove they can catch a planted violation before they judge the real code.
  • Private by default, verified from the template: a pull request that could reopen public access on any data service fails before a human sees it.
  • No credential a person could copy. One managed identity per workload, one database role per identity, passwordless Postgres, and OIDC for deploys pinned to the main branch.
  • Published numbers are measured from the tape, withheld until the sample is large enough, and limited to what the backtest supports.

What commercial grade means here

The product started as two prototypes on a laptop: a signal classifier that paper-traded a chat community and a coil-and-breakout scanner with a small backtest harness. They proved the ideas. They could not be trusted with a customer, because a prototype relies on its author remembering what is safe.

A commercial-grade system removes that dependency. Two people build this platform, and AI coding agents write most of the code, so the standard I held was simple: a change should be safe because the process makes it safe, whoever or whatever wrote it. What follows is the full set of controls, grouped by the layer they protect. Each one is enforced in code, in configuration, or in a pipeline, and the ones that matter most are tested against deliberate violations on every pull request.

The pull-request gate

Every pull request, whatever branch it targets, runs a workflow with read-only permissions and no deploy credentials. It stands up a throwaway PostgreSQL instance, applies the migrations, and runs more than 120 blocking checks. Branch rulesets require the gate to pass and one approving review, and nobody can bypass the gate, administrators included.

Most of the checks are invariants: statements about the codebase that must stay true. Each guard first plants fake violations and fails its own step if it would miss them, so a silently broken guard cannot pass a pull request.

  • Security: every SQL statement is parameterized or carries a reviewed exception; every numeric query parameter is bounded; every JSON web token is signed and verified with an audience; every gated route handler enforces its own entitlement check; the API stays default-deny and its public allowlist stays consistent.
  • Egress: every external hostname in the code is classified in the firewall policy, and every policy entry still exists in the code. Third-party trackers fail the build unless a reviewed exception is recorded.
  • Secrets: no committed template may contain a real value, and every secret reference must appear in the secrets inventory with its rotation fields.
  • Data isolation: a test calls the real data functions as one member against another member's ids and requires the other member's rows to come back untouched. Any new member-owned table must be registered for that test. The read-only analyst role is proven unable to read credential tables, write, or call privileged functions.
  • Dependencies: hashed lockfiles must match a fresh resolve, and audits for both languages fail on a high or critical advisory unless it is waived with an id, a reason, and an expiry date.
  • Business integrity: plan prices in the catalog are mirrored against the live payment processors, marketing copy is checked against the catalog, and the hash of each published legal document must match its database seed.
  • Agent hooks: the hooks that govern AI coding sessions have their own tests, and those tests run in the same gate.

The deploy path

A merge to main is the only route to production. The deploy workflow authenticates to the cloud with OpenID Connect and a federated credential pinned to the main branch, so no cloud secret is stored in the repository host and no other branch can deploy. The runner lives inside the private network and leaves through the same egress firewall as the application, on its own allowlist.

  • The image is built once and tagged with the commit, then pushed to a private registry over its private endpoint.
  • Migrations run first, as a job on the same image. A failed migration stops the deploy before the application rolls. Migrations are additive only, so the running code stays correct on the new schema and rollback means redeploying the previous image.
  • The web app rolls with its origin fence, replica range, and scale rule re-pinned, then every scheduled job is re-pointed at the same image. A job that fails to re-point fails the run instead of being skipped.
  • A production smoke check then asserts the exact HSTS preload header, the absence of server fingerprints, the redirect chain on the apex domain, that login refuses a request without a token, that the API is still default-deny, and that the contact route validates before it mails.
  • New replicas receive no traffic until a readiness probe confirms their market caches are warm.

The network and identity

The whole estate is defined in sixteen infrastructure templates, and posture changes only through a pull request and a deploy. There is one way in and one way out.

  • In: an edge service with a web application firewall in prevention mode, the managed default rule set plus a bot-manager rule set, request body inspection, and per-route rate limits for login, checkout, the AI connector, and anonymous data. Traffic reaches the container environment only over Private Link; public access on the environment is disabled.
  • Out: a deny-by-default firewall whose rules are generated from a reviewed allowlist file in the repository. Route tables force the application, runner, and management subnets through it. A second check asserts the firewall has no broad service-tag or wildcard rules.
  • Private endpoints and private DNS zones for the database, the key vault, the registry, and storage. A template-level check resolves the public-access setting of every storage account, vault, registry, environment, and database server to disabled before merge.
  • Twelve managed identities, one per application and job, each mapped to its own database role with its own grants. The database accepts identity tokens only; password authentication is disabled on the server, and it has zero firewall rules because it has no public address.
  • Human access through a bastion to jump hosts with no public IP, identity-based SSH, password login disabled, and deallocation between uses. The analyst host carries an identity with no roles, a network rule that reaches only the database, and a nightly shutdown.
  • Delete locks on every alert rule, the registry, the uptime tests, and the public DNS zone. Key vault with purge protection. TLS 1.2 minimum at every edge.

The data layer

The database is the system of record for members, billing, alerts, and the track record, so its controls are the strictest.

  • Additive-only migrations, applied automatically before each deploy. A destructive change ships as two deploys: the code stops using a column first, and the drop lands later.
  • Append-only tables for alert events, billing events, and audit history, enforced by row-level triggers plus a statement-level trigger that blocks truncation. Both are set to fire for every session, so a replica connection cannot silence them.
  • Versioned product rows: a price change closes a version and inserts a new one. Nothing is edited in place.
  • The database client refuses to connect a non-production process to the production host without an explicit override, and requires verified TLS on every connection.
  • A scrubbed snapshot job exports a copy for local development under a select-only role. Scrubbing happens in SQL at export time, and the export aborts if it meets a column that has not been classified.
  • Thirty-five days of point-in-time restore, with the restore drilled and written up as a runbook alongside break-glass and member-data-incident procedures.

The application perimeter

The web application assumes the edge might be bypassed and defends itself in middleware, in order.

  • An origin fence requires the edge service's identifying header on every request, with a documented kill switch. Every non-canonical host receives a permanent redirect.
  • A cross-site origin check returns 403 on authentication, account, and billing routes. The API is default-deny: anonymous requests get 401, a missing entitlement gets 403, and an inactive account sees a narrow allowlist.
  • Strict security headers, with HSTS on the preload list, and a two-tier content security policy: sensitive paths get a per-request nonce with strict-dynamic, prerendered pages get a fixed policy, and violation reports feed an alert.
  • Rate limits in layers: at the edge, per replica, shared across replicas in the database, and a separate anonymous limit. Ten failed logins lock an address, and a site-wide rule flags a password spray.
  • Sessions are signed tokens with a separate audience per token family, a rotation overlap window for the signing secret, and revocation through a per-member version. The signature is checked at the edge and the member is rechecked in the database on every protected call.
  • Optional time-based one-time-password MFA with the secret encrypted at rest and single-use recovery codes. A bot check guards self-serve signup.
  • Administration is single sign-on only, through the identity provider with a federated managed-identity credential, so the app registration holds no client secret. An admin row must already exist; sign-on never provisions one.
  • Payment webhooks are verified against the processor's signature on the raw body, then written to an idempotency ledger so a replayed event cannot double-apply.

The AI connector

Members can connect their own AI assistants to the platform over the Model Context Protocol. Giving an agent access to member data is exactly where a prototype cuts corners, so this surface got its own authorization server.

  • OAuth 2.1 with PKCE (S256 only), dynamic client registration, discovery documents, and resource indicators. Codes live five minutes and are single use; access tokens live one hour and are bound to the connector as their audience.
  • Authorization codes and refresh tokens are stored as hashes only. Refresh tokens rotate, and replaying a rotated token revokes the whole grant.
  • A bearer guard rechecks member status, token version, grant revocation, and plan on every call, and fails closed on any database error. Limits are keyed per member rather than per IP address, with a separate cap on client registration.
  • Every tool declares whether it is read-only, destructive, idempotent, or open-world, and the types enforce the declaration. Every answer is shaped to the member's plan before it leaves, and the server wraps every result with an as-of timestamp and the disclaimer so no individual tool can forget them.

Detection and response

Prevention is never complete, so the estate watches itself. Logs flow to a central workspace with extended retention on identity tables, and the rules that read them are treated as production code.

  • Scheduled log-query rules across identity, data plane, network, control plane, liveness, and cost, each delete-locked and each with a documented trigger that produces a known email.
  • A daily heartbeat job sends a test alert through the whole relay, and liveness and silent-pipeline rules turn a quiet log source into an alert of its own.
  • Two watcher jobs run every fifteen minutes: one reads the DNS security layer for blocked critical lookups, bursts, devices gone dark, and policy drift; the other reads the secrets vault's event feed for unexpected reads and bursts.
  • An endpoint-protection custom detection flags the secrets CLI when it is launched by an unexpected parent process.
  • A secrets inventory records every credential, resource credential, human login, and keyless integration with its rotation fields, and the CI gate checks the code against it.
  • Cost alerts, a monthly budget, and an anomaly rule, delivered over a channel that still works when the site is down.

Data observability

A market-data product is only as honest as its freshest feed, so feed health is instrumented like uptime.

  • Every fetch writes a feed event and every scheduled run writes a job record, throttled and fire-and-forget so instrumentation can never slow the request.
  • A sampler judges every feed every five minutes under a database advisory lock, so exactly one replica renders the verdict. It probes feeds nobody is currently reading, emails once on red and once on recovery, and has its own silent-sampler alert.
  • An operations board shows ninety daily cells plus the last twenty-four hours of ticks per feed.
  • Availability tests run browser-style from multiple regions with body-content matching, because a curl that returns 200 says nothing about whether a real page rendered.

Honest numbers

The last layer of controls protects the member from us. A platform that publishes performance has every incentive to round up, so the rules for measurement are written in the legal text and enforced in code.

  • The chart-pattern engine was replayed point in time across years of history before anything shipped. The replay showed no directional edge, so the product claims pattern geometry and never prediction.
  • Scanner filters are gated on measured data. A control whose field has not been populated is disabled with an explanation instead of returning a misleading empty table.
  • Entry is the price published with the alert. Exit is the first price the exit sweep observed at or through the level. Open positions never count in the statistics.
  • No win rate is published below twenty closed alerts, and averages below ten are marked provisional. Published alerts are archived, never rewritten, and the append-only event history survives any administrative action.
  • Summaries drop any figure the source filing does not state, and an unreadable source is reported as unreadable rather than as "nothing on record."

Governing the agents

The coding agents work under written rules that live in the repository, and the rules are backed by hooks that run before any tool call.

  • The rules: every change ships through a pull request with branch protection on, administrators included; never widen production access; an agent proposes a change and stops, and a blocked task is an acceptable outcome; migrations are additive; no credentials in files.
  • A pre-execution hook denies any attempt to run code against production: starting a job with an overridden command, executing inside a container, writing to infrastructure templates, or piping a download to a shell. A second hook denies edits to the hooks themselves.
  • Deny rules block force pushes, secret and variable changes on the repository host, and killing processes by name.
  • The hooks have their own test files, and those tests run in the pull-request gate, so a hook that stopped working would fail the build.
  • A non-engineer co-founder ships through the same gate. The gate does not know or care who opened the pull request.

The point

AI-assisted development is a legitimate way to find out what to build, and most of what I know about this product came from the prototypes. Commercial grade is a property of the process around the code: identity instead of passwords, invariants instead of reviews that depend on attention, append-only records instead of trust, and detection for the cases prevention misses. Building that process is the work I do.

Stack

TypeScriptNext.jsPythonPostgreSQLAzure Container AppsBicepGitHub ActionsClaude Code

The same build, inside your organization.

One person, architecture to deployment. Tell me what is stuck and I will tell you where I would start.