Identity & Auth
Identity & Auth
In production, you can’t let anyone submit intents, access memory, or manage agents without control. Kernova has identity and authorization built in from day one — not bolted on later.
The Problem
Most AI frameworks have zero security model. In development that’s fine, but in production:
- Who submitted this intent? (accountability)
- Should this user be allowed to access organizational memory? (authorization)
- How do I give my frontend limited access without exposing everything? (scoping)
- How do I revoke access if a key leaks? (security)
How Kernova Handles It
Every actor in the system — whether human, AI agent, or external service — has an identity with roles and permissions.
Identity (who are you?) → Roles (what group do you belong to?) → Permissions (what specific actions can you take?)Identities
Register users, agents, and services:
// Register a human userconst alice = kernel.identity.registerIdentity({ type: 'user', name: 'Alice', email: 'alice@company.com', roles: ['developer'],});
// Register an AI agent identityconst assistant = kernel.identity.registerIdentity({ type: 'agent', name: 'assistant', // Gets 'agent' role by default (memory + tools only)});
// Register an external serviceconst webhook = kernel.identity.registerIdentity({ type: 'service', name: 'slack-webhook', roles: ['viewer'], // Read-only access});Authentication
API Keys (for programmatic access)
Generate keys scoped to specific permissions:
const { plaintext, key } = kernel.identity.createApiKey({ name: 'production-frontend', ownerId: alice.id, permissions: ['intent:submit', 'memory:read'], ttlSeconds: 86400 * 30, // Expires in 30 days});
// plaintext = "nxs_a1b2c3d4..." (shown ONCE, never stored)// Use in Authorization header: "Bearer nxs_a1b2c3d4..."Key security:
- Keys are prefixed with
nxs_for easy identification - Stored as SHA-256 hashes — plaintext is never saved
- Can be revoked instantly:
kernel.identity.apiKeys.revoke(key.id) - Optional TTL for automatic expiration
Sessions (for web dashboard)
For the Studio UI or your own web frontend:
const session = kernel.identity.createSession({ identityId: alice.id, permissions: ['studio:access', 'intent:read', 'event:read', 'memory:read'], ttlSeconds: 86400, // 24 hours});
// session.token = "abc123..." (use as Bearer token)Authenticating a request
// From an API keyconst auth = kernel.identity.authenticateWithKey('nxs_a1b2c3d4...');if (auth) { console.log(`Authenticated as: ${auth.identity.name}`); console.log(`Permissions: ${auth.permissions}`);}
// From a session tokenconst auth = kernel.identity.authenticateWithSession('abc123...');Authorization (Permissions)
Permission format
Permissions follow the pattern resource:action:
memory:read — Can read memoriesmemory:write — Can store memoriesintent:submit — Can submit intentsintent:read — Can view intent resultsagent:execute — Can trigger agent executionagent:manage — Can register/remove agentstool:execute:* — Can execute any tooltool:execute:search — Can only execute the 'search' toolworkflow:approve — Can approve workflow stepsstudio:access — Can access the web dashboardevent:read — Can query events* — Superuser (everything)Built-in roles
| Role | Permissions | Typical use |
|---|---|---|
admin | * (everything) | System administrators |
developer | intents, memory, agents, tools, workflows, events, studio | Engineers building with Kernova |
agent | memory read/write, tool execute, event emit | AI agents during execution |
viewer | read-only (intents, memory, agents, events, studio) | Dashboards, monitoring |
Checking permissions
// Check (returns result, doesn't throw)const result = kernel.identity.checkPermission({ identityId: alice.id, resource: 'memory', action: 'write',});// { granted: true, matchedRule: 'memory:write' }
// Require (throws if denied)kernel.identity.requirePermission({ identityId: webhook.id, resource: 'memory', action: 'write',});// → throws: "Permission denied: Identity 'slack-webhook' lacks permission 'memory:write'"Custom permissions
Grant specific permissions directly:
kernel.identity.permissions.grantPermission(webhook.id, 'intent:submit');kernel.identity.permissions.revokePermission(webhook.id, 'intent:submit');Enforcement
Permissions are automatically enforced by the kernel:
// When submit() is called with an owner:await kernel.submit({ goal: 'Do something', owner: webhook.id });// → Kernel checks: does webhook.id have 'intent:submit'?// → If not: throws "Permission denied"Budget limits are also enforced per-identity — see the Budget guide.
Organizations
Group identities together for team access:
const org = kernel.identity.createOrganization({ name: 'Kernova Labs', slug: 'Kernova-labs', ownerId: alice.id, // Alice gets 'admin' role});
// Add team memberskernel.identity.addOrgMember(org.id, bob.id, 'developer');kernel.identity.addOrgMember(org.id, assistant.id, 'agent');
// List membersconst team = kernel.identity.getOrgMembers(org.id);Studio Auth
In development, Studio (http://localhost:4200) is open by default. In production, enable auth:
// In production.ts (generated by Kernova deploy)new StudioServer(kernel, { requireAuth: true, // All /api/* routes require Bearer token});Access with: Authorization: Bearer nxs_your_key
The /health endpoint is always public (for load balancer probes).
Event Auditing
All identity actions emit events:
identity.registered— New identity createdidentity.permission.denied— Access was blockedidentity.role.assigned— Role changedorganization.created— New org
Query these for audit logs:
const denials = kernel.events.query({ type: 'identity.permission.denied', limit: 100 });Best Practices
- Use API keys for services, sessions for humans — Different TTLs and revocation patterns
- Scope keys tightly — Give each key only the permissions it needs
- Set TTL on keys — Leaked keys auto-expire
- Audit permission denials — Monitor
permission.deniedevents for abuse attempts - Use organizations — Group related identities for easier management