Authorize

#Shares & Grants

Helm has two independent levers for access, and you usually reason about them separately:

Tier Lever Between Actions Where it comes from
1 Share two tenants read only A manifest the owner offered and the other tenant accepted
2 Grant a tenant and one of its principals any of create / read / update / delete on your own resources, read on shared ones /access/grants, or the …/{id}/grants route of the resource

A share is visibility, not permission. It makes a resource visible to another tenant. Who inside that tenant may use it is a separate decision, covered by grants and roles.

The step-by-step guides are Share with Another Tenant (the owner) and Accept & Activate (the receiving tenant).

Authenticate with a Personal Access Token (HTTP Basic auth). Enter it once in the Authorize bar above and every Try it below uses it.

#Tier 1: shares (cross-tenant, read-only)

A share says that one resource of the owner's tenant (the grantor) is readable by one other tenant (the grantee). Shares are never written directly. They come from manifests:

  1. The owner lists resources in a manifest and offers it to the grantee.
  2. The grantee accepts.
  3. Every resource the manifest resolves to becomes a share from owner to grantee. It stays shared while at least one accepted manifest still lists it.

The contract is the same for every resource type:

  • ✅ Makes a resource's existence and shape visible to the grantee.
  • ✅ Lets the grantee use a shared definition, for example activate a shared workflow or app spec in their own tenant. The activation belongs to them.
  • ❌ Never lets the grantee write to the owner's data.
  • ❌ Never copies the owner's data into the grantee tenant.
  • ❌ Never lets the grantee re-share onward.

If you want create or update on a share, you need a different lever. Cross-tenant writes are the grantee acting on its own rows of a shared type, or a workflow that runs in the owner's tenant under a policy the owner controls.

#What a share of each type conveys

Shared resource The grantee can
entity_type See the type and its attribute schema, and store its own rows of the type. The owner's rows stay hidden
entity See the owner's rows: all rows of a type, or one row. Who inside the grantee reads them is tier 2 (below)
json_schema Read a standalone schema, for example one a shared definition names in configSchemas
relationship_rule See the rule, and use it to link your own rows to the owner's rows (below)
workflow_definition Read the workflow and activate it. This includes reading the jobs its author owns and bound on it
app_spec Read the spec by id and activate it. This includes reading the jobs its author owns and bound on it
job_definition Read that job version, and bind it

Two rules add to that table:

  • An entity share brings its type. Sharing rows also shares the entity_type they belong to, so the grantee can make sense of them.
  • A listing-only share (permitted_actions: []) makes the resource visible without granting read.

Types and rows are separate on purpose. You can share a type, so a partner can store their own rows of it, without exposing your rows.

#Why a share exists: sources

GET /access/shares lists every share in and out of your tenant, each with the sources that justify it:

GET/access/sharesList shares in and out of your tenantAPI docs ↗Try it

Filter with direction: inbound (shared with you), outbound (shared by you), or all (the default). Each source names a manifest and publication, plus a provenance:

provenance The resource is shared because…
member the accepted manifest lists it
include:<manifest id> a manifest the accepted one includes lists it
pairing:<member id> it is the entity type of an entity member

One resource shared between the same two tenants is always one share. If several manifests list it, or one manifest lists it twice (for example as a member and as a paired type), the share has several sources. It ends when the last source goes: the owner withdraws the offer, removes the member, or deletes the manifest, or the grantee declines.

#Tier 2: grants (authorization inside a tenant)

A grant authorizes a principal (a user, a team, or a policy) to act on one resource, optionally narrowed by an ABAC attribute_filter.

  • On your own resource, actions may be any of create, read, update and delete.
  • On a resource another tenant shares with you, a grant gives read only, and it needs the share. A grant with any other action, or on another tenant's resource that isn't shared with you, is refused.
POST/access/grantsGrant a principal actions on a resourceAPI docs ↗Try it

/access/grants is the tenant-wide view. You can also manage grants from the resource itself. Entity types, relationship rules, JSON schemas, workflow definitions, job definitions and app specs each have a …/{id}/grants route (GET, POST, and PUT / DELETE on …/grants/{grantId}). There the resource is taken from the path, and actions defaults to ["read"]:

POST/entity/entity-types/{id}/grantsGrant a principal access to one entity typeAPI docs ↗Try it

A resource holds one grant per principal. Posting again for the same principal returns the existing grant (200) unchanged, so change its actions with PUT …/grants/{grantId}.

These routes appear in the API reference under the resource's Grants tag (Entity Type Grants, Relationship Rule Grants, Schema Grants, Workflow Grants, Job Grants, App Spec Grants).

Jobs follow bindings. Reading a job follows the binding that pins it, not a grant. An app spec's policies block can't grant job_definition at all, because a hand-written grant would only widen access beyond the jobs the app actually runs.

#Combining the tiers

Within one tenant, a grant is all you need. To read another tenant's rows you need both: a share from the owner (tier 1) and permission in your tenant for the principal (tier 2):

   tier-1 share (owner → consumer)            tier-2 grant (consumer → principal)
A ──(rows of type X visible to tenant B)──► B ──(user u may read type X)──► user u

For tier 2, grant the principal read on entity with the shared type's id, or with a shared row's id. When the owner shares all rows of a type, a role that reads all entities (such as tenant_admin or viewer) covers them too. A token bound to a policy always needs the grant.

If the share goes (the owner withdraws, or you decline), the cross-tenant read stops on the next request, even though your grant is still stored. If you remove the grant, that principal loses access while the share remains.

A share never lets you write the owner's rows, but you can link your own rows to them. A link is authorized on the types of its two endpoints:

  • Create a link: read on both endpoint types, plus update or create on the type of your own endpoint.
  • Remove a link: read on both endpoint types, plus update on the type of your own endpoint.
  • One endpoint must be your own row. A link between two rows that are both shared with you is refused with 403, whatever your grants.

read on the shared endpoint is the combination above: the share plus your grant. The link uses a relationship rule, and a rule's two types always belong to the tenant that authored it, so the owner shares the rule (relationship_rule), the type your rows use (entity_type, which lets you store your own rows of it), and the rows you link to (entity). See Entities & Data Model for the requests and their refusals.

#See also