Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.steward.fi/llms.txt

Use this file to discover all available pages before exploring further.

Cross-Tenant Identity

Steward supports multi-tenant authentication. A single user identity can belong to multiple apps (tenants), each with its own agents, policies, and configuration. The user keeps the same wallet and credentials across all apps.

How It Works

When a user signs up through any auth method, Steward creates a user record tied to their email or wallet address. This user can then join additional tenants without creating new accounts.
User (user@example.com)
├── Tenant: "trading-app"    → role: admin
├── Tenant: "nft-gallery"    → role: member
└── Tenant: "defi-dashboard" → role: member
The JWT access token includes a tenantId claim that scopes all API operations. Switching tenants refreshes the JWT with a new tenant context.

Join Modes

Each tenant controls how new users can join:
ModeDescription
openAnyone can join by calling joinTenant(id)
inviteUsers must be invited by a tenant admin
closedNo new members allowed

SDK Usage

List Tenants

const auth = new StewardAuth({
  baseUrl: "https://api.steward.fi",
  storage: localStorage,
});

// After signing in...
const tenants = await auth.listTenants();
console.log(tenants);
// [
//   { tenantId: "trading-app", tenantName: "Trading App", role: "admin", joinedAt: "..." },
//   { tenantId: "nft-gallery", tenantName: "NFT Gallery", role: "member", joinedAt: "..." },
// ]

Switch Tenant

Switching tenants refreshes the session JWT with a new tenantId claim:
const session = await auth.switchTenant("nft-gallery");
if (session) {
  console.log("Now in:", session.tenantId); // "nft-gallery"
} else {
  console.log("Switch failed, user may need to re-authenticate");
}

Join a Tenant

const membership = await auth.joinTenant("defi-dashboard");
console.log(membership);
// { tenantId: "defi-dashboard", tenantName: "DeFi Dashboard", role: "member", joinedAt: "..." }

Leave a Tenant

await auth.leaveTenant("defi-dashboard");
Users cannot leave their personal/default tenant. Attempting to leave the tenant they signed up with will throw an error.

React Usage

Tenant Picker

The <StewardTenantPicker> component renders a dropdown or list for switching between apps:
import { StewardTenantPicker } from "@stwd/react";

// Dropdown variant (compact, click to expand)
<StewardTenantPicker
  variant="dropdown"
  onSwitch={(tenantId) => console.log("Switched to:", tenantId)}
/>

// List variant (always visible, good for settings pages)
<StewardTenantPicker variant="list" />
The component shows the active tenant, lists all memberships, and handles switching via auth.switchTenant(). It auto-fetches tenant memberships when the user signs in.

User Button with Tenant Switcher

The <StewardUserButton> can include an inline tenant switcher:
import { StewardUserButton } from "@stwd/react";

<StewardUserButton
  showTenantSwitcher  // adds tenant switching inside the dropdown
  showWallet          // show wallet address
  onSignOut={() => router.push("/login")}
/>

Login with Tenant Context

Pass a tenantId to <StewardLogin> to scope authentication to a specific app:
<StewardLogin
  tenantId="trading-app"
  showGoogle
  showPasskey
  title="Sign in to Trading App"
/>

API Endpoints

EndpointMethodDescription
/user/me/tenantsGETList all tenants the current user belongs to
/user/me/tenants/{tenantId}/joinPOSTJoin an open tenant
/user/me/tenants/{tenantId}/leaveDELETELeave a tenant
/auth/refreshPOSTRefresh with { refreshToken, tenantId } to switch tenant

Data Isolation

Each tenant is a fully isolated environment:
  • Agents are scoped to a tenant. Agent IDs are unique within a tenant.
  • Policies are set per-agent, per-tenant.
  • Secrets and routes are tenant-scoped.
  • Transaction history is per-tenant.
The user’s wallet address is shared across tenants, but all operational data is isolated.