Features

Multi-Tenancy

How organizations, spaces, members, end-users and API keys fit together, and how Appstrate isolates tenants on every request.

Appstrate is multi-tenant by design. Every request is evaluated in a strictly scoped context, and every row carries its tenant keys. This page gives the map. The pages it links to give the detail.

The hierarchy

Organization (UUID)
├── Members (owner | admin | member | guest)
├── Custom roles, models, provider credentials, proxies, package catalog
├── Space spc_xxx (team spaces, one is the default)
│   ├── Space members (role per person) and visibility
│   ├── Packages placed here and which are active (agents, skills, integrations)
│   ├── Runs, files, schedules, memory, notifications
│   ├── Integration connections (per member or per end-user)
│   ├── End-users eu_xxx
│   ├── API keys apst_xxx
│   └── Webhooks (space level)
└── Personal space of each member (private)
LevelWhat it isPage
OrganizationMembership, billing and shared infrastructure boundary.Organizations
SpaceWhere agents run. Scoping and access boundary inside the organization.Spaces
MemberA person with an organization role and, per space, a space role.Roles and permissions
End-userAn identity from your own product, scoped to a space.End-Users
API keyA headless credential bound to one space.API Keys

The platform does not use row-level security in the database. Isolation is enforced by the application: every query filters by organization, and by space for space-scoped resources. There is no query that crosses tenants implicitly.

How a request is scoped

Each authenticated request goes through the same steps, in order:

  1. Authentication. Module strategies (OAuth tokens) are tried first, then an API key (apst_), then the cookie session.
  2. Organization context. The organization comes from the key, or from the X-Org-Id header for a session, and the caller must be a member. With an API key the X-Org-Id header is ignored.
  3. Permissions. The organization role is resolved, then the credential's ceiling (key scopes) applied.
  4. Space context. For space-scoped routes (agents, runs, schedules, end-users, API keys, notifications, packages, integrations, files, uploads), the space comes from the key, or from X-Space-Id for a session. An X-Space-Id that contradicts the key is a 403. The caller's role in that space is resolved and the effective permissions become (organization ∪ space) ∩ ceiling. No role in the space is a 403 not_a_space_member, or a 404 for a private space.
  5. API version and idempotency, then the route handler.

Routes of the /api/orgs/{orgId} family carry the organization in the path instead of a header. Webhooks name their own space in the body or query. See Spaces for the transports.

Acting for an end-user

Send Appstrate-User: eu_xxx with an API key to act as one of your own customers. The call is then attributed to the end-user, uses its connections and memory, and only sees its runs. It is refused for cookie sessions and for end-users of another space. See End-Users.

Realm isolation

Sessions carry a realm. Dashboard users have the realm platform. When the oidc module signs in the end-users of a space, those sessions get the realm end_user:<spaceId>. A guard rejects an end-user session on platform routes and the reverse, so a stolen end-user session cannot reach an organization's admin API even though both live on the same instance.

Credential isolation

Credentials (OAuth tokens, API keys, custom fields) are encrypted at rest with CONNECTION_ENCRYPTION_KEY and stay scoped to a space and to their owner, a member or an end-user. At run time the agent never receives a credential. The run's sidecar injects it on outbound requests (see Sandbox and Sidecar). A member's connection is theirs. Others in the space use it only when it is explicitly shared or pinned by an admin. The details are in Isolation and Security.

Audit

Three records answer "who did what":

  • Run records. Every run stores its organization, space, the key, member, end-user or schedule that launched it, and the model and proxy it used. See Runs.
  • Structured logs. Authentication decisions, permission denials and every Appstrate-User impersonation are emitted as JSON log lines.
  • Audit table. State-changing operations (organization, space, member, key, webhook, package and connection changes) are appended to an audit_events table with actor, resource, before and after values, IP and request id. Writes are best effort so that an audit failure never blocks a change. The table is not exposed through the API, so export it from the database or forward the logs for retention and tamper-evidence.

Mapping your product

Your productAppstrate
Your companyAn organization
An environment or a product lineA space
Your backendAn API key bound to a space
One of your customersAn end-user, with your id in externalId
A customer triggers an agentYour backend calls the API with Appstrate-User
A customer's Gmail or Slack accountA connection owned by the end-user
Your support team using the dashboardMembers with space roles

One organization with one space is usually enough. Add spaces when you need separate agents, schedules, keys and connections between environments or teams, and separate organizations when customers need full isolation.

On this page