Skip to content

Tenant Controller — domain resolution schema (abridged)

Reverse-engineered, and out of our ownership — nova-tenant-controller owns this database; we document only the slice Super Agent depends on for domain resolution, not the full schema. Passed the schema-first gate for that slice.

Diagram

erDiagram
    INSTANCES ||--o{ TENANTS : "hosts"
    TENANTS {
        string tenant_id PK
        string instance FK
        string tenant_name
        string sys_domain
        string user_domains "::-delimited email domains, LIKE-matched"
        boolean is_active
    }
    INSTANCES {
        string sn_instance PK
        string encryption_key_id
        string sn_jwt_provider_sys_id
    }

resolve_tenant(email) in nova-a2a-adapter splits email to get a domain, matches it against TENANTS.user_domains, then follows TENANTS.instance to INSTANCES.sn_instance to find the ServiceNow instance to call.

What resolves a user to a ServiceNow instance

tenants.user_domains (TEXT, ::-delimited, e.g. ::novaworks-ai.biz::) is the field that maps an email domain to a tenant. Looked up via get_tenant_by_user_domain() in nova-agent-utils:

Tenants.user_domains.like(f"%::{domain}::%")

tenant_idinstance (FK to instances.sn_instance) then resolves the tenant to its ServiceNow instance. Read/write for user_domains goes through tenant-controller's own updateTenant API — there's no separate lookup endpoint there; the lookup query above lives entirely on the consumer (Python) side.

The gap: we're not using hd

Nothing here needs to change. nova-a2a-adapter's resolve_tenant() derives the domain by splitting the user's email address (email.split("@")[-1]) instead of reading Google's verified hd (hosted-domain) claim, which is already present in the validated JWT payload. This is a caller-side fix in nova-a2a-adapter, not a schema change — user_domains already supports arbitrary domains per tenant.

Note

Two systems can write this database independently (tenant-controller's TS migrations, and a separate Python create_all() path in nova-agent-utils for a couple of tables tenant-controller's migrations don't create). Since we don't own this schema, that's flagged here only in case it ever affects Super Agent's read path — not something for us to resolve.