---
title: Multi-Tenancy
description: How Shield scopes scans, policies, and all safety entities to app and tenant contexts.
---

Every Shield entity is scoped to an **app** (logical application boundary) and optionally to a **tenant** (user/organization boundary). This scoping is enforced at the store layer — cross-tenant access is structurally impossible.

## Context injection

Scope identifiers are injected into the Go context using helper functions from the root `shield` package:

```go
import "github.com/xraph/shield"

ctx = shield.WithTenant(ctx, "org-123")
ctx = shield.WithApp(ctx, "myapp")
```

## Extraction

Retrieve scope values from any context:

```go
tenantID := shield.TenantFromContext(ctx) // "org-123"
appID    := shield.AppFromContext(ctx)    // "myapp"
```

Both functions return an empty string if no value is set.

## AppID vs TenantID

| Scope | Purpose | Required | Example |
|-------|---------|----------|---------|
| **AppID** | Logical application boundary. Safety primitives (instincts, awareness, boundaries, values, judgments, reflexes, profiles) carry an `AppID` field. | Yes | `"myapp"`, `"staging"` |
| **TenantID** | User or organization boundary. Scan results and PII tokens are scoped by tenant for data isolation. | Optional | `"org-123"`, `"user-456"` |

## Store enforcement

Store implementations enforce scoping on every query:

- **Instincts, Awareness, Boundaries, Values, Judgments, Reflexes, Profiles** — all `GetByName` and `List` queries filter by `app_id`
- **Scans, PII Tokens** — queries filter by `tenant_id` when provided
- **Policies** — scoped by `scope_key` and `scope_level` (app or org)
- **Cross-scope access** — returns `ErrNotFound` even if the entity exists under a different app/tenant

## API integration

When using the Forge extension, the API layer extracts the app ID from the request context (typically set by Forge middleware) and passes it to all engine operations. This means:

- Each API request is automatically scoped to the caller's app
- Safety profiles in app A cannot be accessed by app B
- Scan history is isolated per tenant within each app

## Example: multi-tenant setup

```go
// Tenant 1 scans content
ctx1 := shield.WithApp(context.Background(), "myapp")
ctx1 = shield.WithTenant(ctx1, "tenant-1")
result1, _ := eng.ScanInput(ctx1, &scan.Input{Text: "hello"})

// Tenant 2 scans content
ctx2 := shield.WithApp(context.Background(), "myapp")
ctx2 = shield.WithTenant(ctx2, "tenant-2")
result2, _ := eng.ScanInput(ctx2, &scan.Input{Text: "hello"})

// Each tenant's scan results are isolated
// Tenant 1 cannot see tenant 2's scan history
```
