Platform architecture
Haste Health isolates data on two levels: Tenants are the top-level organizational boundary, and Projects partition resources within a tenant. Every request, token, and resource belongs to exactly one tenant and one project, no exceptions.
Tenants and projects
Every tenant gets exactly one System Project. It holds identity, login config, and the Project record for every project in the tenant; access rules, OAuth clients, and clinical data all live in regular projects like the two on the right.
| Level | Isolates | Identified by |
|---|---|---|
| Tenant | Identity providers, users, and the Project record of every project underneath it | Tenant ID, first path segment |
| Project | Access policies, OAuth clients, memberships, FHIR resources, search | Project ID, second path segment |
A resource in one project can't reference a resource in another, and a tenant's data is never visible to another tenant. Access tokens are scoped to a tenant, and, once a user's membership is checked, to specific projects within it.
The System Project
Every tenant gets one reserved project, system, created automatically and
impossible to delete or rename. It holds tenant-level resources, including
the record of every other project in the tenant:
- IdentityProvider — external OIDC providers for federated login
- User — accounts and their tenant-level role (
admin,member, orowner) - Project — metadata for every project in the tenant, user-created ones included
{
"resourceType": "User",
"email": "bob@myhealth.health",
"role": "admin"
}
Regular projects
Everything else lives in regular, user-created projects, scoped per project rather than per tenant:
- AccessPolicyV2 — the access a user or client has within that project. See Access control.
- ClientApplication — OAuth clients associated with that project
- Membership — links a
system-projectUserto this project - Clinical and application data:
Patient,Observation, and every other FHIR resource
How a request resolves
User signs in through that project's OAuth client.
Credentials and Membership are checked.
Token is scoped to accessible project(s).
AccessPolicyV2 in that project is applied.
FHIR response is returned.
Membership resources (in each project) determine which projects a user can reach; that project's own AccessPolicyV2 then determines what they can do once there. See Identity & access control for the full model.
URL structure
https://api.haste.health/w/{tenant}/{project}/api/v1/fhir/{resourceType}/{id}
↓ ↓ ↓ ↓
Tenant Project Resource Resource ID
GET /w/acme-health/production/api/v1/fhir/Patient/123 # a project's data
GET /w/acme-health/system/api/v1/fhir/User/user-456 # the System Project
Working with projects
### Create
POST /w/{tenant}/system/api/v1/fhir/Project
Content-Type: application/fhir+json
{ "resourceType": "Project", "name": "MY CLINIC", "fhirVersion": "r4" }
### List
GET /w/{tenant}/system/api/v1/fhir/Project
### Delete (permanent; the reserved `system` project can't be deleted)
DELETE /w/{tenant}/system/api/v1/fhir/Project/cardiology
Projects are managed through the System Project's API, but each one is a fully separate data container once created.
Choosing project boundaries
Common ways teams split projects within a tenant:
- Environment:
development,staging,production - Department:
cardiology,oncology,emergency - Application:
patient-portal,clinical-emr,analytics - Customer: one project per customer, for SaaS deployments
Keep related resources together within one project. References can't cross project boundaries.