Skip to content

Backend Base Architecture

Version: 1.0.0
Audience: junior backend developers, frontend developers, QA, DevOps, tech leads
Status: architecture and enforcement reference


This document explains how the Base Backend is supposed to work in the new architecture.

It does not try to be the full endpoint catalog.
Its job is to explain:

  • what the Base Backend owns
  • what the Base Backend does not own
  • how Auth and Platform Core fit into runtime access control
  • how Authorization and x-org must be handled
  • how effective access is resolved
  • how request enforcement must work
  • what must fail closed
  • what old logic is deprecated and must be removed

This document is intentionally detailed so junior developers can understand the architecture without guessing.


The platform is split into multiple backends with different responsibilities.

Owns:

  • business features
  • business CRUD
  • business workflows
  • artists
  • events
  • vendors
  • venues
  • tasks
  • offers
  • dashboard-related business data
  • other non-auth, non-commercial business domains

Does not own:

  • login
  • JWT issuance
  • password flows
  • company membership
  • effective access
  • packages
  • add-ons
  • subscriptions
  • commercial entitlements

Owns:

  • identity
  • users
  • login
  • JWT issuance
  • JWT refresh
  • memberships
  • tenant roles
  • permissions
  • invitations
  • effective access resolution

Owns:

  • packages
  • add-ons
  • subscriptions
  • module catalog
  • package/add-on catalog
  • company commercial entitlements

Owns:

  • financial records
  • accounting truth
  • settlements
  • final expense/income authority where applicable

Base Backend
= business execution
Auth Backend
= identity + membership + permissions + effective access
Platform Core Backend
= commercial ownership + entitlements
Financial Backend
= final finance/accounting truth

This split is not optional.
This is the architecture the Base Backend must follow.


The Base Backend must:

  • receive a bearer token from the client
  • receive x-org from the client
  • validate the JWT
  • treat x-org as the active company context
  • obtain effective access from Auth
  • verify the basic module is enabled
  • verify the route-specific basic.* permission exists
  • allow or deny the request
  • execute business logic only after access checks pass

The Base Backend must never:

  • log users in
  • issue tokens
  • refresh tokens
  • reset passwords
  • create invitations
  • decide membership locally
  • decide whether the company owns Basic
  • decide module access locally from old package tables
  • decide permission access locally from old permission tables
  • read Platform Core directly for runtime user authorization
  • allow access because the frontend hid or showed a button
  • allow access when Auth resolution fails
  • guess a default company if x-org is missing

The source of truth for who the user is is the Auth Backend.

The Base Backend must trust Auth for:

  • user identity
  • token issuer
  • membership
  • effective access

All protected Base Backend requests must include:

Authorization: Bearer <JWT_FROM_AUTH_SERVICE>
x-org: <COMPANY_ID>

This token is:

  • issued by Auth
  • consumed by Base
  • never created by Base

This is the active company context for the request.

The Base Backend must:

  • read it
  • validate it
  • use it consistently
  • reject the request if it is missing or malformed

The Base Backend must validate JWTs issued by the external Auth service.

For every protected request, validate:

  • token presence
  • token structure
  • signature
  • expiration
  • issuer
  • audience
  • token format
  • session validity if the chosen middleware supports that check

JWT validation in Base is not:

  • local login
  • local credential validation
  • token generation
  • token refresh

Any old local JWT/auth code in Base is deprecated and must be removed:

  • /auth/* handlers
  • local login services
  • token issuing helpers
  • refresh flows
  • password reset flows
  • auth DB logic in Base

Only JWT consumption/validation middleware is allowed.


The active company is provided through:

x-org: <COMPANY_ID>

For all tenant-scoped routes, Base must:

  1. read x-org
  2. require it
  3. validate it
  4. treat it as the active company for the request
  5. use it for all downstream access decisions

Base must not:

  • invent a default company silently
  • resolve company from local session state
  • use a company chosen in Base as the access authority
  • decide membership locally
  • decide commercial entitlement locally

Whether the user belongs to the company in x-org is decided by Auth, not Base.


The source of truth for effective access is Auth Backend.

Auth resolves effective access by combining:

  • company entitlements from Platform Core
  • user grants / roles / permissions from Auth

Conceptually:

effectiveAccess = entitlements ∩ userGrants

Effective access must not be computed unless the user already has a valid membership in the company.

Membership is created through:

  • invitations in Auth
  • admin actions in Auth

Base should enforce against an effective access model that answers questions like:

  • does this user have access to company X?
  • is basic enabled?
  • does the user have basic.artist.view?
  • does the user have basic.event.create?

Base must not:

  • derive effective access from JWT alone
  • infer access from old local package logic
  • use local permission tables as truth
  • call Platform Core directly for runtime per-request authorization

The Base Backend is the backend for the Basic/Core business application.

That means every protected business route in this service must require:

  • the basic module
  • the appropriate basic.* permission

Examples include:

  • basic.dashboard.view
  • basic.artist.view
  • basic.artist.create
  • basic.artist.edit
  • basic.artist.delete
  • basic.event.view
  • basic.event.create
  • basic.event.edit
  • basic.event.delete
  • basic.calendar.view
  • basic.calendar.edit
  • basic.vendor.view
  • basic.vendor.create
  • basic.vendor.edit
  • basic.vendor.delete
  • basic.venue.view
  • basic.ticketing.view
  • basic.ticketing.create
  • basic.workspace.view
  • basic.workspace.create
  • basic.workspace.edit
  • basic.workspace.delete

No basic module → no Base app access.
No route permission → deny the route.


For every protected Base Backend request:

  1. Receive request
  2. Read Authorization
  3. Read x-org
  4. Validate JWT
  5. Validate x-org
  6. Resolve effective access from Auth
  7. Confirm membership exists for x-org
  8. Confirm basic is present in effective modules
  9. Confirm required basic.* permission exists
  10. Execute business logic
  11. Return response

You should fail early and clearly:

  • missing JWT → 401
  • invalid JWT → 401
  • missing x-org400
  • malformed x-org400
  • no membership → 403
  • no basic module → 403
  • no permission → 403
  • Auth access resolution unavailable → 503

The Base Backend must operate under a strict fail-closed model.

If Base cannot verify any of the following:

  • JWT validity
  • company context
  • membership
  • effective access
  • permissions

Then the request must be denied.

Never:

  • allow temporarily
  • guess access
  • infer permission from UI visibility
  • fallback to old package logic
  • fallback to local role tables
  • fallback to stale assumptions without an explicit approved design

A fail-open design becomes a security bug very quickly.
If Auth or access resolution is unavailable, access must stop.


Use when:

  • x-org is missing
  • x-org is malformed
  • request shape is invalid

Use when:

  • JWT missing
  • JWT invalid
  • JWT expired
  • JWT rejected by validation middleware

Use when:

  • user is authenticated but not allowed for this company
  • basic module missing
  • permission missing

Use when:

  • effective access cannot be resolved from Auth
  • Auth service unavailable
  • network failure or timeout blocks access resolution

When returning 503, Base must fail closed.


The following domains are deprecated in Base and must not be used as live authorities:

  • Auth
  • Company Users
  • Company User Invitations
  • Company Teams
  • Packages
  • Permissions
  • Role
  • Subscription
  • Users

These domains may still exist in old code, old Swagger groupings, or old files.

That does not mean they are valid.

All logic related to those domains must be removed from Base:

  • routes
  • controllers
  • services
  • repositories
  • tables used as source of truth
  • access checks based on those local domains
  • Auth / Users / Invitations / Membership / Teams / Roles / Permissions → Auth Backend
  • Packages / Add-ons / Subscriptions / Entitlements → Platform Core Backend

15. How Base should think about other services

Section titled “15. How Base should think about other services”

Base depends on Auth for:

  • identity
  • access control
  • company membership
  • effective access

Base depends on Core only indirectly through Auth for runtime authorization.

That means:

  • Auth talks to Core
  • Base talks to Auth
  • Base does not call Core directly for user access checks

Base may coexist with financial-related read/write domains, but it is not the final accounting authority where the Financial Backend is the source of truth.


Before adding any new Base endpoint, ask:

  1. Is this business-domain logic or auth/commercial logic?
  2. Does this route require basic module?
  3. What is the exact basic.* permission for the route?
  4. Does the request require x-org?
  5. Does the route fail closed if Auth cannot resolve access?
  6. Am I accidentally using old local tables for access?
  7. Am I duplicating something that belongs in Auth or Core?

If any answer is unclear, stop and resolve it before coding.


These are examples only, but they show the expected pattern.

Route areaModule requiredPermission
Dashboard readbasicbasic.dashboard.view
Artist listbasicbasic.artist.view
Artist createbasicbasic.artist.create
Artist updatebasicbasic.artist.edit
Artist deletebasicbasic.artist.delete
Event listbasicbasic.event.view
Event createbasicbasic.event.create
Event updatebasicbasic.event.edit
Event deletebasicbasic.event.delete
Vendor listbasicbasic.vendor.view
Vendor createbasicbasic.vendor.create
Venue listbasicbasic.venue.view
Workspace/task readbasicbasic.workspace.view

handleRequest(request):
token = readAuthorizationHeader(request)
if token missing:
return 401
companyId = readXOrgHeader(request)
if companyId missing or malformed:
return 400
jwtResult = validateJWT(token)
if jwtResult invalid:
return 401
accessResult = resolveEffectiveAccessFromAuth(token, companyId)
if accessResult unavailable:
return 503
if accessResult.membership != valid:
return 403
if "basic" not in accessResult.modules:
return 403
requiredPermission = mapRouteToPermission(request.route)
if requiredPermission not in accessResult.permissions:
return 403
return executeBusinessLogic(request)

The Base Backend is a business backend, not an identity backend and not a commercial entitlement backend.

Its job is:

  • validate incoming Auth-issued JWTs
  • require explicit company context
  • enforce effective access returned by Auth
  • execute business logic only after access is approved

That boundary must remain strict.