Skip to content

Resource model

Polaris is the single source of truth. Users, teams, databases and data shares are Polaris records, stored in the polaris PostgreSQL database. There is no separate application database. A fresh installation has no teams, users or databases.

Concept Polaris representation Application properties
User Principal with a dedicated principal role portal.name, portal.memberships
Team Principal role marked portal.kind=team portal.name, portal.description
Membership Team IDs and the role per team, recorded on the user principal portal.memberships (JSON: team ID → role)
Role assignment Per catalog, the user's principal role gets the catalog role matching their role in the owning team Polaris catalog-role grants
Database Internal Iceberg catalog with a dedicated RustFS bucket portal.name, portal.team, portal.environment, portal.description, portal.bucket
Data share Principal marked portal.kind=share, with a principal role and a catalog role of the same name portal.database, portal.objects, portal.recipient-team, portal.external, portal.expires-at, …
Automation principal Principal svc-<uuid> marked portal.kind=automation, with principal roles svc-<uuid> (catalog role writer) and svc-<uuid>-read (reader) on each database of its team and environment; the share catalog roles of team shares received in that environment on svc-<uuid>-read portal.team, portal.environment, portal.extension, portal.created-by

Managed records carry portal.managed-by=iceberg-portal-v2.

Data Where it lives
Users, teams, shares, grants and catalogs PostgreSQL polaris database
Iceberg metadata and Parquet data RustFS, one bucket per database
Saved notebooks A Docker volume per team and environment; on Kubernetes a PVC in <namespace>-notebooks
Sessions and notebook state Process and container memory
Service configuration and bootstrap credentials .env; on Kubernetes chart values and the Secret iceberg-platform

Users

A user signs in through Keycloak and is linked by exact issuer and subject to a Polaris principal with a stable portal-<uuid> ID. Usernames follow the form validation rules. Application users are not PostgreSQL login roles.

Teams and roles

A team has a stable team-<uuid> ID, a unique name and an optional description. A user belongs to one or more teams and holds one role per team. That role applies to every database the team owns.

Role Catalog role Access
reader reader Read metadata, table data and semantic models
writer writer Also create and write namespaces, tables and views
admin admin Also manage catalog access, the team's databases and data shares

Effective access is the union across all of a user's teams. The active team and environment only select what the portal and notebooks show. Changing memberships or roles, or moving a database, synchronizes the affected grants.

For example, a user who is writer in analytics and reader in operations can write to the databases of analytics and read those of operations. If a database moves from operations to analytics, that user can then write to it.

Portal administration is a separate Keycloak role (iceberg-admin/platform-admin), and no team role grants it.

Databases

A database is an Iceberg catalog, not a PostgreSQL database. It has a stable db-<uuid> catalog ID, a display name, an owning team, an environment (development, acceptance or production) and a dedicated bucket. Display names are unique within a team and environment.

Renaming a database or moving it to another team keeps its catalog ID, bucket and data. Deletion is immediate in every environment. It revokes the database's shares and removes all stored data, and an interrupted deletion can be resumed.

Data shares

A data share gives another team, an external party, or both read access to selected tables, views and semantic models of one database. The team and external destinations are fixed at creation. A share can also be listed: a listing extension then publishes its metadata to a data catalog. A listing grants nothing, so it can be switched at any time, and a share can be listed only. A share has a stable share-<uuid> ID that names three Polaris records: a principal (the external credential), its principal role and a catalog role in the shared database.

  • Grants: the catalog role holds exactly one grant per selected object: TABLE_READ_DATA on a table, VIEW_READ_PROPERTIES on a view, SEMANTIC_MODEL_READ on a semantic model. It holds nothing on namespaces or the catalog, so external clients load objects by their full names. For team shares, the catalog role is granted to each recipient member's principal role, including future members, and removed when a member leaves.
  • Views are not filters. The recipient's engine reads a view's underlying tables, so those tables must be shared too, and the recipient can read them in full. A share needs at least one table.
  • Semantic models need their tables. A shared model is only useful with the tables it reads. The portal and the MCP tools add those tables when you select a model. The API names each missing table in warnings, and includeModelTables=true adds them instead.
  • Limits and drift: a share holds at most 50 objects, and a database at most 20 shares. Grants follow the object itself, not its name, so a renamed object stays shared, while a dropped and recreated one does not. The portal shows the difference, and saving the share again fixes it.
  • Ownership: a share belongs to its database. Every current Administrator of the owning team can manage it. A shared database can't move to another team until its shares are revoked.
  • Listings: a listed share's principal never receives its principal role, as for a team-only share, so its grants open nothing. The Bridge reads the listed objects' definitions for a listing extension with a temporary catalog role (portal-listing-<uuid>) on the platform identity. That role holds only TABLE_READ_PROPERTIES, VIEW_READ_PROPERTIES and SEMANTIC_MODEL_READ on the listed objects, the reads ask for no storage credentials, and the role is deleted after each read and swept when the Bridge starts. A listing counts toward the 20 shares of its database.
  • Secret and expiry: the client secret is shown once and never stored. New secret ends the previous one immediately. Revocation and expiry end issued tokens at once. The user gateway enforces expiry every 30 seconds, and every share listing enforces it too.

Automation principals

An automation principal is the service account through which an extension, such as Conversational BI, dbt or Ingestion, acts for one team in one environment. A team Administrator enables it through the extension (the Bridge). There is at most one per team, environment and extension. It follows the team's databases in that environment: creating, moving and deleting a database update its grants like a member's. It is never a user, member or portal login. Deleting the team revokes it, and platform administrators can revoke it on the Teams page. Its secret is never stored; the Bridge resets it when it starts and issues short-lived tokens (POLARIS_TOKEN_LIFETIME, one hour by default) that carry either the writer or the read-only role. Team shares that its team received from a database in the same environment are also granted to its read-only role, never to the writer role, and end with the share.