#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:
- The owner lists resources in a manifest and offers it to the grantee.
- The grantee accepts.
- 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
createorupdateon 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
entityshare brings its type. Sharing rows also shares theentity_typethey 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,
actionsmay be any ofcreate,read,updateanddelete. - On a resource another tenant shares with you, a grant gives
readonly, 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
policiesblock can't grantjob_definitionat 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.
#Links to shared rows
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:
readon both endpoint types, plusupdateorcreateon the type of your own endpoint. - Remove a link:
readon both endpoint types, plusupdateon 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
- Share with Another Tenant and Accept & Activate walk the two sides end to end.
- Provisioning covers the tenant-admin surface: users, tokens, roles and policies.
- The Canonical Example shows a share and a grant end to end.