FastAPI v1
The local apps/v1 FastAPI app exposes the migrated public asset registry
surface for this repository.
Startup
The deployment migration Job must finalize application tables before the API
starts. FastAPI attaches the canonical and pricing runtimes in one
api_endpoint_scope(discovery_timeout=300), selected from its Environment. This
bounded startup wait tolerates a waking or replacing shared MetaTables API;
attachment errors still fail startup. The scope closes before serving requests,
which keep their normal discovery budgets. Startup performs no schema migration.
Scope
This API is intentionally thin:
- route declarations, validation, and OpenAPI metadata live under
apps/v1 - provider-neutral collection, discovery, bulk-preflight, operation, and
structured-error machinery lives in the installable
msm.api.httppackage - reusable asset category workflows live under
src/msm/services - asset, category, and index frontend route composition is backed by
src/msm/services/asset_master_lists.py - pricing curve registry, market-data set, and binding workflows are backed by
msm_pricing.api - portfolio detail and latest-weight workflows are backed by
src/msm_portfolios/services - virtual-fund identity and holdings snapshot workflows are backed by
src/msm/services/accounts/virtual_funds_public_api.py
Route Reference
- Settings Route: read-only app settings and runtime assumptions for frontend clients.
- Account Routes: route group for account identity, holdings snapshots, and target-position assignment.
- Asset Routes: route group for the asset registry and asset categories, including the Command Center monitor frame.
- Index Routes: route group for index registry reads, delete-impact preflight, and index delete.
- Calendar Routes: route group for calendar identity CRUD, summary, and date, session, and event maintenance.
- Pricing Market Data Routes: route group for pricing market-data set and concept binding management.
- Fixed Income Pricer Routes: route group for method-backed bond pricing operations over assets with current pricing details.
- Pricing Curve Routes: route group for pricing curve registry lists.
- Portfolio Routes: route group for portfolio identity, detail-page composition, latest weights, and delete operations.
- Portfolio Group Routes: route group for many-to-many portfolio classification and membership lookups.
- Portfolio Signal Metadata Routes: route group for signal metadata list/detail/write operations and signal-weight storage cleanup.
- Virtual Fund Routes: route group for account-owned virtual-fund identity and holdings snapshots.
- Command Center Bulk Actions: SDK-contract discovery, preflight, and execution for destructive collection actions.
- Command Center Resource Contracts: the canonical collection, discovery, detail, summary, and action boundaries for all lists.
- Provider Adapter HTTP Toolkit: installable collection, discovery, preflight, observable-operation, and error helpers for provider adapters.
Design Decisions (ADRs)
These FastAPI v1 decisions live in the single ADR tree:
- Calendar CRUD And Summary Route: route group for calendar identity CRUD, summary, and bounded date, session, and event maintenance.
- Command Center Adapter Discovery:
additive Adapter from API discovery contract for Command Center without
breaking existing
/api/v1clients. - Fixed Income Pricer API: registry-driven pricing workbench API for price, analytics, duration, yield, z-spread, cashflows, carry/roll-down, curve preview, and fixings availability.
- Reusable Delete Impact Contract: shared preflight serializer and route pattern for inspecting individual destructive delete effects before deletion.
Runtime Bootstrap
apps/v1 performs startup-time runtime attachment instead of waiting for the
first request to hit a row operation. MSM_AUTO_REGISTER_NAMESPACE may override
the namespace for local development; when it is not set, the runtime uses the
default markets namespace from msm.settings.markets_namespace().
Current local-dev behavior:
- the app calls
msm_portfolios.start_engine(...)during startup for theapps/v1table set because this surface includes portfolio-backed account target-position routes - the startup table set includes portfolio-backed target-position tables, so target-position routes resolve against the existing shared markets runtime instead of starting a second portfolio runtime on first request
- the startup table set includes
PortfolioMetadataandPortfolioWeightsStorageso portfolio detail and latest-weights routes use the same shared markets runtime - the startup table set includes
VirtualFund,VirtualFundHoldingsSet, andVirtualFundHoldingsStorageso virtual-fund routes attach to the shared markets runtime - the app calls
msm_pricing.bootstrap.attach_pricing_schemas(...)during startup for the pricing rows used by asset pricing details, curve registry lists, and pricing market-data management - the pricing startup table set includes
AssetPricingDetailsStoragebecause the current-pricing-details row API requires both the timestamped storage table and the current projection table to be attached before row operations - index delete-impact preflight attaches
FutureAssetDetailsandIndexFixingsStorageso the API can report restrictive dependencies before an index delete is attempted - schema mutation must already have been handled by the deployment workflow's
migrate-marketsJob, which appliesmsm_migrations:migrationbefore the API rolls out (locally,metatables --local migrations upgrade) - the app uses the runtime DataSource of the MetaTables API that the caller's Organization Environment selects; it configures no API URL or DataSource of its own
- if that runtime has no usable DataSource, compiled operations raise
metatables.DataSourceResolutionErrorinstead of redirecting writes into an ad hoc local store
Local full-stack debugging
The sibling mainsequencemarketssite repository owns the VS Code compound launcher for local
frontend/API debugging. Its Markets: Full Stack configuration runs
apps.v1.dev_cors:app with the project .venv under debugpy on
http://127.0.0.1:8001, and runs the Vite frontend on http://127.0.0.1:3010 with that exact API
origin. The development wrapper admits ports 3010 and 5173 for both localhost and 127.0.0.1;
the deployed api.main:app surface is not mutated. The launcher leaves
MSM_AUTO_REGISTER_NAMESPACE unset in accordance with the local runtime bootstrap contract.
Locally, request identity checks every request's Authorization: Bearer token against
MAINSEQUENCE_ENDPOINT/api/v1/users/me/, so the frontend must send the signed-in user's token;
a request without one receives 401.
Platform Deployment
The application implementation remains under apps/v1. The thin
api/main.py module re-exports the same FastAPI app object because Main
Sequence discovers deployable FastAPI resources from api/**/main.py paths.
It contains no route, schema, service, or runtime logic.
The release is managed by
.mainsequence/workflows/ms-markets-api.yaml. The declaration uses workflow API
3.0.0, retains three release revisions for rollback, and its
automatic-redeployment policy deploys every pushed commit of the tracked branch
(tag_regex: null). The backend resolves the verified image for
the exact eligible commit, so the workflow must not contain a
related_image_uid. It requests the standard API capacity of 0.25 vCPU and
0.5 GiB on non-spot infrastructure. The release admits the supported
https://*.site-dev.main-sequence.app origin so the Main Sequence Markets
static-site release can use the Command Center SDK delegated FastAPI transport.
The frontend identifies this release by its stable ResourceRelease UID; the SDK
resolves the current opaque RPC endpoint at request time after every automatic
API redeployment.
The same file declares the migrate-markets Job, which follows the same
every-push policy, and the order of each deployment: the image step prepares
the markets-api image, migrate runs jobs/migrate_markets.py from that
image, and deploy_api deploys the API with needs: [migrate]. A failed
migration blocks the rollout and the previous release keeps serving; see
Hosted Deployments.
Both workloads declare the reader policy on the MetaTables branch
(access.branches), which lets them find the Environment's MetaTables API.
Required in every Environment
The Job and the API run as their own workload Users. The Job migrates the
ms-markets tables only with the editor role on the mainsequence.markets
MetaTables namespace, and the API reads and writes their rows with
data_writer. The namespace's owners grant these roles in each Environment;
workflow entries can't. See
MetaTables access in each Environment.
Publish repository changes with a plain git push of the tracked branch; the
workflow files decide what deploys. The tracked main branch changes only through
release pull requests from development; see Releasing. Since Main Sequence SDK 9.0.5,
mainsequence code-repository sync only refreshes local dependencies
(uv lock, uv sync, uv export --locked --no-dev --no-hashes); it does not
bump the version, tag, commit, or push. The push starts the backend-owned image
build, the migration Job, and the release rotation; use the deployment-run
interfaces to verify the terminal state and logs instead of treating the Git
push alone as deployment success.
Runtime dependencies must be resolvable from the backend build environment.
The published ms-markets 2.x package therefore declares
mainsequence>=9.0.29,<10 and mainsequence-metatable>=0.1.51,<0.2 without
exact patch pins. The lower bounds enforce the SDK 9 and MetaTables client
extraction hard cut, the identity-only caller assertions the platform signs (SDK
9.0.27 and later; 9.0.25 and 9.0.26 are excluded), and a MetaTables client that
works with SDK 9.0.30 and knows the namespace and table roles (0.1.51), while the
project lock and exported runtime requirements select the exact releases validated
for this repository: mainsequence 9.0.30 and mainsequence-metatable 0.1.51.
MetaTables 0.1.51 retains batched migration inspections and a five-minute migration
API discovery budget, and refreshes changed authored metadata at unchanged
revisions. Metadata finalization requires the corresponding shared MetaTables API
update. Do not replace the published dependencies with machine-local
[tool.uv.sources] path overrides.
create_app() installs the SDK request identity with
install_request_identity(app); the platform launcher refuses to serve an
application without it. It authenticates every request before its handler: a
hosted request by the gateway's signed caller assertion, a local request by its
Bearer token. Handlers read the verified caller from request.state.user (a
RequestUserIdentity with the canonical uid and is_organization_admin) or
User.get_logged_user(). The platform's caller assertions carry only those two
facts, for the caller and for a requester; an assertion that lists team_uids is
refused with 401. Membership is checked on demand with
RequestUserIdentity.in_teams(), which no Markets route needs. Route code does
not parse authentication headers or bind SDK request-header context variables.
Every handler and service acts for one identity (ADR 0047, decision 3):
apps.v1.identity.acting_identity() returns User.get_requester() on a
requester-bound call and User.get_logged_user() otherwise, never both, and
None outside an authenticated request. The Index routes derive their actor from
it; the deprecated IndexActor.team_uids stays empty for HTTP callers. Every
service that creates, updates or deletes a registry row is wrapped with
apps.v1.identity.registry_write, which resolves the acting identity before the
write and, after a successful write, records resource, action, row uid and the
acting identity's uid and kind on the apps.v1.identity logger. No registry
table has an author or owner column yet; when one does, the same hook supplies
its value. Writes keep running as the Markets workload, whose data_writer
role on mainsequence.markets is the single place writes are allowed. Who may
trigger them is the platform's grant: an application's workload reaches the
Markets API only when its workflow grants it the MainSequenceMarkets branch
(see the client guide), and every caller
the API admits, person or application, may trigger registry writes.
A requester is only present when the markets-api resource declares
acts_for_requester: true in .mainsequence/workflows/ms-markets-api.yaml.
That declaration lets the platform sign the person an application works for
into the caller assertion. Only an Organization admin can enable it: the
declaration takes effect only when the person who pushes the workflow file is
an Organization admin. A push by anyone else makes the markets-api resource
fail before it deploys, so every push of a branch that carries the declaration
must come from an Organization admin.
Without the declaration, every call is caller-bound and acting_identity()
returns the caller, the calling application's workload User.
API Discoverability
GET /openapi.json- includes Redocly-compatible
info.x-logometadata for Main Sequence Markets branding - declares every operation summary and description, all used tags, stable tag groups, and the API source repository
- derives canonical row and field descriptions from the backing MetaTable metadata instead of maintaining a second description inventory in the FastAPI layer
- uses the local emblem served by this FastAPI app at
/static/main-sequence-markets/main_sequence_markets_icon_emblem_transparent.png GET /docs- serves the Swagger UI for interactive inspection
GET /redoc- serves the ReDoc view; consumers that support
info.x-logocan render the configured logo GET /health- returns a zero-argument health payload for API discovery
- response is
{ status, service, version } - does not touch MetaTables, pricing runtime data paths, or request identity
GET /.well-known/command-center/connection-contract- returns the Adapter from API discovery contract for the existing
apps/v1FastAPI operations - references
/openapi.json - lists every public
/api/v1/*operation by its existingoperationId - classifies read/calculation operations as
query - classifies create/update/delete/write operations as
mutation - disables cache metadata for mutation operations and non-GET calculations
- keeps provider-native responses provider-native and does not publish
responseMappings - advertises
getAssetMonitorFrameas a directcore.tabular_frame@v1query operation for Command Center Asset Monitor workspaces
Resource contract cutover
All list operations return command-center.resource_collection@v1, and each
has an authoritative sibling /discovery/ operation. The retired
response_format selectors, DRF-style pagination envelopes, and standalone
/bulk-actions/ discovery endpoints are not part of the API.
The nested category asset table should use GET /api/v1/asset/ with
categories__uid. The dedicated POST /api/v1/asset/query/ route is still
future work for this local API.
Validation
The focused FastAPI coverage for this surface lives under:
tests/msm/fastapi/v1/
Use /openapi.json, /docs, and /redoc from the local app for contract
inspection.
Export a deterministic reviewed snapshot for downstream documentation or client generation with:
.venv/bin/python -m scripts.export_apps_v1_openapi --output /absolute/path/to/openapi.json
The downstream repository owns its pinned snapshot. Its production documentation build must not fetch a live API release or import this repository at runtime.