Security & Permissions
Opteryx uses workspaces as the default permission boundary. Within a workspace, access is granted with a role on a resource pattern - a glob over collection.dataset (e.g. analytics.* for every dataset in a collection, or analytics.sales for one dataset). A grant on * covers the whole workspace.
Roles and capabilities
| Action | reader | writer | owner |
|---|---|---|---|
Query a dataset (SELECT) |
✅ | ✅ | ✅ |
| Insert / append rows | ✅ | ✅ | |
| Create a new table or view | ✅ | ✅ | |
| Truncate a table (remove all rows) | ✅ | ✅ | |
| Drop a table or view | ✅ | ||
| Drop a collection | ✅ | ||
CREATE OR REPLACE an existing table |
✅ | ||
ALTER TABLE ... CLUSTER BY |
✅ | ||
ALTER TABLE ... RENAME TO |
✅ | ||
ALTER WORKSPACE ... SET |
✅ | ||
View a table's manifest (SHOW MANIFEST FOR) |
✅ | ||
| Grant or revoke access to other users | ✅ |
Each role includes everything the role below it can do - owner implies writer, which implies reader.
A couple of things that surprise people:
writercan truncate a table. Opteryx doesn't yet support row-levelUPDATEorDELETE FROM ... WHERE, soTRUNCATE TABLE(a full wipe) is the only "delete" primitive that exists, and it's granted at the same tier as insert. There's no way to grant append-only access that's protected from truncation.writercannot replace a table.CREATE OR REPLACE TABLEhas the same blast radius asDROP TABLE- the existing data and history are gone - so it requiresowner, even thoughCREATE TABLEfor a brand-new table only requireswriter.writercannot change a table's clustering.ALTER TABLE ... CLUSTER BYchanges the table's physical layout rather than its contents, so it sits at the sameownertier asDROP TABLE, not thewritertier that governs inserts and truncates.DROP COLLECTIONchecks the grant against the collection's own name, not a pattern over its contents. Anownergrant onworkspace.staging.*covers every table and view inside thestagingcollection but does not matchworkspace.stagingitself, so it does not permit dropping the collection. You need a grant that matchesworkspace.stagingdirectly - an exact grant on it, or a workspace-wideworkspace.*.ALTER WORKSPACEneeds ownership of the workspace itself, and owning its contents is not enough. This goes one level further than theDROP COLLECTIONrule above: even a workspace-wideworkspace.*grant does not permit it, because that pattern covers everything in the workspace without matching the workspace's own name. You need a grant matchingworkspacedirectly, or a global*. Workspace properties govern the whole workspace, so the grant has to be scoped to it.ALTER TABLE ... RENAME TOis checked at both ends. It needsowneron the source (the table stops existing under that name) and create permission at the target, so owning a table does not let you move it into a collection you have no grant on.- There's also an
adminrole, but it only applies to the Policy API - an admin can view and manage other users' grants on a pattern they administer. It does not grant any query access on its own; an admin who also needs to run queries needs a separatereader/writer/ownergrant.
Workspace boundaries
Workspaces are the primary isolation and billing boundary. A grant on a broader pattern (e.g. the whole workspace) applies to every dataset it matches unless a narrower, more specific grant exists.
Two schemas are handled specially and can't be targeted by a policy:
public.*is read-only for everyone, regardless of any grant - you can't be givenwriterorownerthere.personal.<username>.*is fully owned by that user - no grant is needed, and no one else can be granted access to it.
Policy API
Grants are managed via the Policy API, which accepts JSON policy documents identifying a principal, a role, and a resource pattern.
Example - granting writer on every dataset in the sales collection:
{
"principal": { "identity": "bastian" },
"role": "writer",
"pattern": "sales.*"
}Audit & Logging
All access is audited at query time. Audit logs are retained according to the account's retention settings and are accessible to workspace owners.
For API-level authentication, see the Authentication API.