07

The Backend: one secure core

A build case study of Orca, SpatialX's gigapixel cancer-AI platform. • The backend is one deliberately boring core, with tenant isolation built into the plumbing so a developer cannot forget it.

The backend is deliberately boring

The backend is the server-side half of the platform: the code and data stores the pathologist never sees directly. On Orca it is deliberately boring, and that is a design choice, not an accident. A tiny team cannot also be a 24/7 operations team, so every backend decision optimised for "nothing surprising at 3 a.m." over "clever." The whole thing reduces to one shape: a request comes in through a single front door, one core service handles it, and state lives in three ordinary AWS stores.

The map below is the entire backend. The frontend (the pathologist's browser) talks to exactly one thing — the API Gateway — which hands the request to the core, and the core is the only thing that touches the databases and queues.

The single door: one API Gateway

An API Gateway is a managed AWS service that sits in front of your backend: every request from the outside world arrives there first, and it forwards each one to your code. Orca uses it as the only way in. The frontend never opens a database connection, never calls a compute box, never signs its own AWS request — it can only speak to this one door. Two React apps (the main explore app and a secure-S3 image viewer) both funnel through it.

That single boundary is what keeps the client dumb and the backend in control of both the UI and the data. There is exactly one place to enforce authentication, one place to apply tenant scoping, and one surface to reason about when you think through security.

   MAIN app          pathologist          secure-S3 viewer
       │                  │                       │
       └──────────┬───────┴──────────┬────────────┘
                  ▼                   ▼
            ┌─────────────────────────────┐
            │         API GATEWAY         │   the ONE door
            └──────────────┬──────────────┘
                           ▼
                   ┌───────────────┐
                   │ BACKEND CORE  │   nothing else is reachable
                   └───────────────┘

One core service, in Python on Lambda

AWS Lambda is "serverless" compute: you upload a function, AWS runs it on demand, and you pay only while it runs — there is no fleet of servers to patch or scale, and it costs almost nothing when idle. The Orca backend core is a single Python service (Flask over SQLAlchemy) that runs on Lambda behind that one gateway.

Running the core on Lambda suits the workload: backend logic is mostly short request-and-response work that should cost nothing when nobody is using it and never needs a machine kept warm and patched. It also suits the team: with no fleet to operate, the two engineers spend their hours on product instead of on infrastructure. Heavier or oddly-shaped work does not live here — it gets its own home, which we come back to at the end of this day.

Where state lives: RDS, S3, SQS

The core owns three ordinary stores, each for one job. RDS MySQL is a managed relational database — rows and tables — and it is the source of truth for everything structured: projects, images, annotations, workflow state. S3 is object storage — big files addressed by a key — and it holds the gigapixel slides, the DZI tiles, and the AI model artifacts. SQS is a message queue used for asynchronous work, which the next section covers.

The stack is intentionally a short list of well-worn AWS pieces; each choice is the cheapest, lowest-maintenance option that does the job.

ConcernTechnology
Language / frameworkPython, Flask, SQLAlchemy
State (source of truth)MySQL on RDS
Objects (slides, tiles, model artifacts)S3
The single doorAPI Gateway
AsyncSQS + EventBridge
IdentityCognito
SecretsSecrets Manager

Async without a server: SQS + EventBridge polling

Some work is too slow to finish inside a single request — tiling a gigapixel slide, running a model. That work is asynchronous: the request returns immediately, and the slow part happens later. Orca does this with SQS (a durable queue that holds a job until something picks it up) and EventBridge (a scheduler that fires on a timer). The core publishes a job to the queue; on each EventBridge tick, a consumer wakes up, pulls waiting jobs, and runs them.

The point here is only that the mechanism is boring and serverless — no message broker to run, no always-on worker fleet. There is no clever real-time push; a timer polls a queue. The full engine that rides on top of this — how a job becomes an ordered list of steps, and why the queue is only transport while the database holds the truth — is the whole of Day 08.

Tenant isolation: org_id, injected once

Orca is multi-tenant: many labs share one platform, and each lab's data must stay invisible to every other lab. Because the images are patient tissue, a leak across tenants is the worst thing that can happen. The tenant identifier is the org_id — a value that names which lab a request belongs to. Cognito, the AWS identity service, mints a signed token for each logged-in user, and that token carries the caller's org_id as a claim, so tenant identity travels with every request.

The non-obvious move is where the org_id is applied. Every database read on Orca flows through one resource layer — a single shared piece of code between the endpoints and the ORM. That layer reads the org_id off the token and merges it into the query filter automatically. So every read is scoped to the caller's org by construction. A developer writes a plain query with no WHERE org_id = ... clause, and therefore cannot forget one — the platform adds it in the single place all reads pass through. That is exactly what let junior engineers ship safely on a PHI product.

# What a developer writes — no org filter anywhere:
images = Image.query.all()

# What the resource layer actually runs, every time:
#   SELECT * FROM images WHERE org_id = <org_id from the Cognito token>

Row-Level Security as defense-in-depth

The isolation above lives in the application layer — the resource layer adds the filter. A careful design does not stop there: it assumes the application layer might one day be bypassed and puts a second, independent guard closer to the data. That second guard is Row-Level Security (RLS): a rule enforced by the database engine itself that a session may only see rows matching its org_id, regardless of what query arrives.

RLS is defense-in-depth — the engine independently enforces the tenant boundary even if a bug or a raw query slips past the resource layer. Alongside it, all queries are parameterised (values are bound, never string-concatenated) so a crafted input cannot break out of the org scope through SQL injection. The application layer makes isolation the default; RLS makes it a property the database will not let you violate.

Monolith core, extract only when earned

A monolith is one deployable service that does many things; microservices split those things into many independently deployed services. Orca kept the core a monolith on purpose and extracted a separate service only when a piece genuinely earned its own lifecycle — its own scaling curve, its own runtime, or its own deploy cadence that differs from the core's.

Most backend logic stayed in the one core. The pieces that broke out did so because they are shaped differently from short request-response work: gigapixel-slide tiling is bursty and containerised, so it runs on Fargate; AI inference needs a GPU, so it runs on EC2; PHI obfuscation runs at the edge before ingest. Everything else stayed put. The trade is explicit — you give up pure microservice independence and buy a system a tiny team can actually hold in its head.

                 ┌───────────────────────────┐
                 │        BACKEND CORE       │   one monolith
                 │  projects · images · ...  │   (Python on Lambda)
                 └─────────────┬─────────────┘
                               │  extract ONLY when it earns a lifecycle
        ┌──────────────────────┼──────────────────────┐
        ▼                      ▼                       ▼
  ┌────────────┐        ┌───────────────┐      ┌────────────────┐
  │ DZI tiling │        │  AI inference │      │  Obfuscation   │
  │  Fargate   │        │   EC2 GPU     │      │ service (edge) │
  └────────────┘        └───────────────┘      └────────────────┘

Why this shape fits a tiny team

Read the whole backend as one idea: one door, one core, three ordinary stores, and isolation applied in a single place. Nothing here is clever, and that is the payoff. A boring core is a core a new engineer can understand in a day and operate without heroics.

The isolation-by-construction is the part that mattered most for a small team on a PHI product. Because the org_id filter is added in the plumbing rather than at each endpoint, correctness does not depend on fifty different code paths each remembering a WHERE clause. Juniors ship endpoints safely by default, seniors are freed to spend their time on the genuinely hard parts — the AI models and the workflow engine — and the system stays legible enough that no one person is a bottleneck for running it.

Key takeaways

  • The backend is deliberately boring: one door, one core, three ordinary stores — chosen so a tiny team can operate it without a dedicated ops function.
  • Everything enters through a single API Gateway; the frontend never touches a database or compute directly, which gives you one place to enforce auth and tenant scope.
  • The core is one Python service on Lambda — serverless, cheap when idle, no fleet to patch — because backend logic is mostly short request-response work.
  • State splits cleanly: RDS MySQL is the structured source of truth, S3 holds slides and model artifacts, and SQS + EventBridge carry asynchronous work.
  • Tenant isolation is the one non-obvious thing: the Cognito token's org_id is injected into query filters in the resource layer, so every read is org-scoped automatically and a developer cannot forget the filter.
  • Row-Level Security and parameterised queries are defense-in-depth — the database enforces the org boundary even if the application layer is bypassed.
  • The core stays a monolith; services are extracted only when they earn their own lifecycle (Fargate tiling, EC2 GPU inference, edge obfuscation), not for their own sake.

Checklist

  • [ ] I can explain why "deliberately boring" is a feature, not a limitation, for a tiny team.
  • [ ] I can describe the single-door pattern and why the frontend only ever talks to the API Gateway.
  • [ ] I can say why the core runs on Lambda and what kind of work does not belong there.
  • [ ] I can map each store to its job: RDS (source of truth), S3 (objects), SQS + EventBridge (async).
  • [ ] I can explain how org_id is injected in the resource layer and why that makes tenant isolation impossible to forget.
  • [ ] I can describe Row-Level Security as defense-in-depth and why parameterised queries matter.
  • [ ] I can state the rule for extracting a service — "only when it earns its own lifecycle" — and give the three examples that did.