Reference

Security & Compliance

What's protected, how, who's responsible — for security review.

This page is for security reviewers, IT admins, and anyone who has to answer "is it safe to bring this in?"

Threat model in one sentence

ObjectOS runs as a single Node.js process that talks to one database — inside your network on ObjectOS Enterprise, in ours on ObjectOS Cloud. Self-managed, its only outbound call of its own is license validation, and Enterprise air-gapped licenses validate offline. The blast radius of a compromise is the data on the database it connects to — nothing more.

Data residency

This table describes ObjectOS Enterprise, the self-managed edition. On ObjectOS Cloud the same classes of data live in the infrastructure we operate for you, and on either edition your ontology is exportable to the open-source ObjectStack runtime.

Data classLives inLeaves your network?
Business recordsYour databaseNo
User accounts, sessions, OAuth tokensYour databaseNo
Audit logYour databaseNo
Settings, API keys, secretsYour database / your secret managerNo
Uploaded filesYour disk or an S3-compatible bucket (S3, R2, MinIO)No
The compiled app definition (objectstack.json)A file on disk, or fetched from your control planeOptional
The runtime itselfYour servers or containersNo
Telemetry / usage data—None collected

Beyond the integrations you explicitly configure (OIDC discovery, email provider, AI provider, webhook targets, external storage), the one outbound call self-managed ObjectOS makes on its own is license validation — Enterprise air-gapped licenses validate offline, so an air-gapped deployment makes none. See License & Pricing.

Encryption

LayerMechanismResponsibility
In transit (browser ↔ ObjectOS)TLS, terminated at your edge / ingressYou
In transit (ObjectOS ↔ database)Driver-level TLS (Postgres sslmode=require, MongoDB tls=true, …)You — set the connection string
At rest (business data)Database-native (e.g. Postgres TDE, RDS encryption)You
At rest (uploaded files)Storage-native (S3 SSE, R2 default, disk-level FDE)You
Secrets in DB (settings, OIDC client secret)Encrypted by the settings serviceObjectOS
Session cookies / tokensHMAC-signed with OS_AUTH_SECRETObjectOS
API key valuesHashed in DB — leaked DB row cannot reconstruct the keyObjectOS

Authentication

Built-in (via @objectstack/plugin-auth, powered by Better Auth):

  • email/password with verification + reset
  • session management with revocation
  • social OAuth (Google, GitHub, Microsoft, Apple, …)
  • enterprise OIDC/SSO (Okta, Entra ID, Keycloak, Ping)
  • two-factor (TOTP)
  • passkeys / WebAuthn
  • magic links
  • phone number + SMS OTP sign-in and password reset (opt-in via auth.plugins.phoneNumber; Aliyun / Twilio transports, rate-limited)
  • CLI/browser device flow
  • API keys (hashed, expirable, revocable, bound to a user)
  • self-serve OAuth 2.1 for MCP clients (authorization-code + PKCE, dynamic client registration, scope-derived permission ceiling)
  • admin-driven user creation and bulk import (one-time passwords with forced rotation via must_change_password)

See Authentication.

Authorization

Layered enforcement (via @objectstack/plugin-security), following Permission Model v2 (ObjectStack 13, ADR-0090):

  1. Object permissions — CRUD per object per permission set, distributed to users directly or through flat positions
  2. Org-wide defaults — custom objects with an owner default to a private sharing model; anonymous access to object data is denied unconditionally, with no switch to open it (ObjectStack 17.0). Public access is declared per surface — a public form or a share link — and the guest anchor grants nothing
  3. Row-level security — declarative policy expressions injected into queries; not optional, and a policy the runtime cannot enforce denies rather than widens (ObjectStack 17.5)
  4. Field-level security — fields stripped from responses, partially masked (maskingRule, ObjectStack 17.1), or rejected on write (keys are object-qualified, lint-enforced). A hidden or masked field cannot be filtered, sorted, grouped or aggregated on — 403 at every query door since ObjectStack 17.6
  5. Organization scoping — multi-tenant isolation; bypass requires explicit viewAllRecords

System-context operations bypass checks so internal jobs / migrations can run — these paths are auditable.

Supporting machinery:

  • Explain engine — explain(principal, object, operation) reports every layer's verdict with per-layer attribution, using the same evaluators as enforcement (no drift by construction).
  • Author-time lints — os compile gates the build on security posture (security-owd-unset, security-anchor-high-privilege, security-fls-unqualified-key, …), and an optional committed access-matrix.json snapshot makes any capability drift fail CI until explicitly re-approved.
  • Delegated administration — permission sets can carry an adminScope so sub-admins manage assignments only within their business-unit subtree, from an allowlist, with granted_by audit.
  • MCP scope ceiling — OAuth-connected AI agents run under effective_permission = scope_ceiling ∩ user_grants (data:read / data:write / actions:execute), failing closed.

See Permissions.

Audit & evidence

When the audit capability is loaded (@objectstack/plugin-audit):

  • Every CRUD operation across every object → audit row.
  • Before/after values for field changes.
  • Authentication, permission grants, session revocation events.
  • Audit rows are immutable: cannot be modified, only archived.
  • Retention is declared on the object's lifecycle block (sys_audit_log ships with 90 days hot, then archive) and tuned per environment via lifecycle.retention_overrides; pair with your DB's archive policy.

This is the evidence base for SOC 2 CC6/CC7, ISO 27001 A.12.4, HIPAA §164.312(b), and GDPR Article 30.

Compliance frameworks

ObjectOS provides the technical primitives every common framework asks for. Certification is a property of the deployment, not the software — but the controls map cleanly:

FrameworkWhat ObjectOS gives you
SOC 2Access control (CC6), change management (audit log), encryption (deployment), monitoring (observability), backup (operate/backup)
ISO 27001A.5 policies (RBAC), A.8 asset management (object catalog), A.9 access control, A.12 operations, A.18 compliance
HIPAAAccess controls (§164.312(a)), audit controls (§164.312(b)), integrity (immutable audit), transmission security (TLS)
GDPRArticle 30 records of processing (audit), Article 32 security of processing, Article 17 right to erasure (soft + hard delete supported), data residency (you choose the region)
CCPA / China DSL / Russia 152-FZObjectOS Enterprise in the right region satisfies residency; access controls + audit cover most reporting obligations

ObjectOS itself is not certified because the certification is of a running deployment, not a binary. Your deployment can be certified — many already are.

Secrets handling

SecretWhere to put it
OS_AUTH_SECRETYour secret manager (Vault, AWS Secrets Manager, k8s Secret); inject as env var
Database URL with credentialsSame
OIDC client secretSame
OAuth provider secretsSame
API provider keys (email, storage, AI)Same
Settings stored in DBEncrypted by the settings service at rest

Never bake secrets into the artifact (objectstack.json), the Docker image, the compose file, or Git. The settings UI in Setup shows env-managed values as locked, so operators can't accidentally override them.

Network model

Required inbound:

  • HTTPS from your ingress / load balancer to ObjectOS on :3000 (default).

Required outbound:

  • License validation, for self-managed ObjectOS — Enterprise air-gapped licenses validate offline and need no egress for it.

Required outbound only if you configure these features:

  • Your database (Postgres / Mongo / Turso / …).
  • S3-compatible storage (if storage capability enabled with S3 adapter).
  • OIDC discovery URL (if SSO enabled).
  • Email provider API (Resend / Postmark).
  • AI provider API (OpenAI / Anthropic / Google / …).
  • Webhook targets.

That's the entire egress surface. See Air-gapped for deployments that cut even more.

AI: tools, approvals, isolation

The AI Builder is the most security-sensitive surface you'll expose, so it has its own enforcement layer in addition to everything above.

How AI can mutate state

The model cannot write directly to your database. The only way state changes is by emitting a structured tool call that the AI service receives, validates, and queues. The chain:

user prompt
  → model emits tool call (e.g. add_field { object: 'ticket', name: 'severity', type: 'select' })
  → AI service validates payload against the tool's Zod schema
  → if the tool is "mutating": queue as pending action (no state change yet)
  → human reviewer approves → mutation applied → audit row written
  → if the tool is "read-only": run immediately, response returned to model

There are 11 first-party metadata tools (see Build → AI Builder) plus one action_<name> tool per action its author exposed to AI — ai: { exposed: true } with an ai.description of at least 40 characters, on a type that runs without a UI (script, flow or api); an unexposed action is invisible to agents and refused if called. Every tool — first-party or custom — has the same lifecycle.

Permission keys

KeyGrants
ai:chatHold a conversation; consume models; let the agent call read-only tools
ai:completeRaw completion endpoint (no agent loop)
ai:conversationsList / inspect / delete conversations (own or all, depending on RBAC scope)
ai:agentsManage agent metadata (alongside ai:chat to invoke them)
ai:toolsList the tool catalogue
ai:executeInvoke a tool directly via REST (advanced — normally only ambient agents need this)
ai:readRead the pending-actions queue and model list
ai:approveApprove / reject queued mutations
ai:adminFull AI service administration

The critical split is ai:chat ≠ ai:approve. Give most users ai:chat so the assistant works; reserve ai:approve for the administrators / app owners who should review structural changes. End users can therefore "vibe-build" safely — the worst they can do is queue a bad change someone else must accept.

Tenant isolation

  • Agents, conversations, knowledge bases, and pending actions are scoped to one Environment (tenant). Tenant A cannot see tenant B's conversations, tools the AI proposed, or knowledge corpora — even if the same agent definition was installed from the same marketplace package.
  • The metadata tools (create_object, add_field, …) operate against the active package in the caller's tenant. They cannot reach outside it.
  • Tool inputs are CEL-validated and the engine refuses references to reserved system packages (sys.*) or to other tenants' object names.

Audit events

When the audit capability is loaded:

  • ai.chat.message — every user / assistant message, with model + token count
  • ai.tool.call — tool name, validated input, full output (or error)
  • ai.pending_action.queued — proposed mutation, full diff
  • ai.pending_action.approved / .rejected — who decided, when, why
  • ai.metadata.applied — actual write to the metadata store, with diff

These rows are immutable and can be exported for security review or chargeback. Token counts per (provider, model, user) feed cost attribution.

Prompt-injection posture

Indirect prompt injection (e.g. malicious content in a document the agent retrieves) is a real risk; ObjectOS reduces blast radius by construction:

  • The AI cannot bypass tool validation — even if it's convinced to emit a malicious payload, the Zod schema rejects malformed inputs before they reach the engine.
  • Mutating tools always queue. An injected prompt cannot silently write to the database.
  • Tool calls inherit the end user's permissions, not the model's service-account permissions. A user can never use the AI to do something they couldn't do themselves through the UI or REST.
  • Skills loaded into agents are versioned and explicit — see Build → IDE Skills and Build → AI Builder.

External AI provider data flow

When you configure a provider (OpenAI, Anthropic, …), only the following leaves your network:

  • The conversation history the model needs
  • Tool definitions (names, JSON schemas — no record data)
  • Tool outputs that the model needs to continue (e.g. a query result the user explicitly asked for)

For air-gapped deployments, point the AI service at a local Ollama / vLLM / TGI endpoint and the same flow stays inside your perimeter.

Vulnerability disclosure

Report security issues privately to security@objectstack.ai. We respond within 1 business day. Do not file public GitHub issues for security problems.

Supply chain

  • Pre-built images published from github.com/objectstack-ai/objectos with reproducible build provenance.
  • All @objectstack/* packages have published source on GitHub — Apache-2.0, no obfuscation.
  • Use SHA-pinned image tags (sha-<short>) in production to avoid drift; see Docker.

Suggested hardening checklist

  • TLS terminated at the edge with a real certificate.
  • OS_AUTH_SECRET is 32+ bytes random, in a secret manager.
  • Database connection uses TLS.
  • HSTS enabled after TLS validation.
  • CORS origins explicit (never * with credentials).
  • Rate limits on auth endpoints (10/min/IP recommended).
  • Audit retention matches policy.
  • OIDC for human accounts; API keys for machine accounts.
  • Backup + restore drill executed and timed.
  • Negative tests: cross-org access denied, field security holds, expired session rejects.
  • Image pinned to sha-<short> or semver tag.
  • os doctor clean in CI before each release.

See Production Readiness for the full go-live checklist.