15. Access: teams, sharing and tokens¶
Runnable companion:
examples/12_share_a_dataset.py
Everything you built in the previous chapters is private. A store you import into, a workset you select, a dataset you convert — they are readable by you and by nobody else, until you say otherwise.
This chapter is how you say otherwise, and the rest of the access story around it: what your role lets you do, which team owns what you build, and how a program — CI, a scheduled job, a service — acts for you without your login.
Your role¶
Your role says what kind of call you may make. It is resolved outside Datavo and checked on every route:
| Role | Can |
|---|---|
| reader | browse, read, download |
| contributor | reader, plus create stores, import, attach, build datasets, mint run tokens |
A role never names a resource; a team does. So every request passes two checks: does
your role allow this kind of call, and do your teams reach this resource?
client.get_auth_status() reports both — your roles, and the teams below.
A team owns it¶
A team is a named set of users. Every store, dataset and split set has exactly one owning team, recorded when it is created.
You always have at least two teams: a personal team of just you, and the
reserved global team called everyone, which every authenticated caller
belongs to.
A resource you create lands in your personal team unless you name one. There is no setting to change that — you name the team when you create the resource, where the consequence is in front of you:
Name a team explicitly when you want the work to belong to the team rather than to you:
You have to be a member of the team you name. Nothing else inherits: a store you create by copying another, or by adding samples from three others, belongs to your personal team — never to a source's.
Sharing is one thing: a grant¶
A grant binds one resource to one team at level read or write:
read lets that team read the dataset and its samples. write also lets them
write to it — attach keys, add samples — and confers read. Neither lets them
re-share it or give it away; those stay with the owning team.
To share with one person, name them by their sign-in address. It resolves to their personal team, and a grant to that team is a grant to them:
person = client.resolve_team_for_person("peter@example.org")
client.grant_resource_access("dataset", "voice_train_v1", team=person.id, level="read")
The address must match whole (any case), and the person must have signed in to Datavo
once. The team list does not list people: list_teams() returns your own teams plus the
teams that own something you can read.
Unsharing is the same verb backwards:
Publishing is a grant to everyone¶
There is no "public" flag. Publishing is a read grant to the global team, and
withdrawing is revoking it:
client.grant_resource_access("dataset", "voice_train_v1", team="everyone") # publish
client.revoke_resource_access("dataset", "voice_train_v1", team="everyone") # withdraw
Because it is an ordinary grant, it is reversible like any other, and clients
derive the label you see — private (no grants), shared (grants to named
teams), public (a grant to everyone) — from the grants alone. A write
grant to the global team is refused: implicit membership must never confer write.
Sharing a dataset is enough¶
Read access on a dataset authorizes reading its samples, without granting anything on the stores it was built from:
# `qc` can read every sample in the dataset...
client.grant_resource_access("dataset", "voice_train_v1", team="qc")
# ...and still cannot read the store it came from.
That is the common case, not a loophole — you want to share the training set, not the raw corpus. It works because a dataset is frozen: its membership was fixed at conversion, so the grant cannot silently widen later.
Building is the other way round. Creating a workset or dataset requires read access on what you are building from: each source store, or — when the source is a dataset — that dataset. And because a workset is live, it re-checks on every read: revoke a grant and the next read stops, rather than serving membership the caller can no longer reach.
How a request proves who it is¶
Every request carries a token, and one rule decides which: people log in; programs run on a run token.
| Token | Held by | Authorizes | Lifetime |
|---|---|---|---|
| identity token | you | everything your role and teams allow | ≈1 hour, refreshed silently |
run token (dpt_…) |
a program — CI, a scheduled job, a service | what you may do, minus sharing | fixed, up to 14 days; revocable |
| stream ticket | the SDK | one shard, read once | ≈30 minutes |
Authority only narrows down the table: a run token can do less than you, and a ticket less than either.
Identity token — you¶
datavo auth login authenticates you through Entra and caches the credential, and
DatavoClient() reuses that cache, so constructing the client is the whole of
authentication (chapter 2). Each access token lasts about an hour, and
MSAL renews it silently from the cached refresh token, so interactive use never meets
the limit. It backs every CLI command and client method you run — and it stays with
you. A program gets a run token instead.
Run token — a program acting as you¶
A program cannot renew your login, since Entra renewal is interactive, and must not carry it. Mint the program a run token from your own client:
from datavo_sdk import mint_run_token
token, expires_at = mint_run_token(client, team="speech-research", ttl_seconds=7 * 24 * 3600)
The program hands it to its own client, for the same instance you log in to:
Setting DATAVO_API_BASE_URL to the instance and DATAVO_AUTH_TOKEN to the token does
the same for a script that builds its own client (chapter 2). Either way
the client finds the instance's processing host, the one host that accepts run
tokens, and sends the token's requests there.
A run token acts as you, as a contributor: it reads, creates, imports, attaches, converts and splits whatever your teams reach, resolved on every request — so it loses a team the moment you leave it. Three things set it apart from you:
- It never shares. Granting, revoking and listing grants refuse it with 403: who may read what is a person's decision.
- What it creates lands in its team — the one you mint it for with
team=, or your personal team — when the create call names none. - It permanently deletes only what its own run created. Deleting a store, dataset
or split set its run did not create — even one you made yourself — is refused,
unless you mint the token with
delete_scope="team".
It never refreshes: it works until ttl_seconds runs out — at most 14 days, which is
also the default — or until it is revoked, whichever comes first. Whoever holds the
token can revoke it, the program itself included:
Stream ticket — one shard, once¶
Iterating a workset or downloading a dataset transfers shards over plain HTTP. The
SDK requests a short-lived signed URL for each shard and uses it on your behalf, so
the download path carries no session. You handle a ticket directly only to pass its
download_url to a non-SDK client.
When a request is refused¶
Each status points at a different part of this chapter:
| Status | Means |
|---|---|
| 401 | no credential this host accepts: you are not signed in, or a run token expired or was revoked |
| 403 | you got through, and the message names what stops you — your role, your write access to this resource, or something a run token may not do |
| 404 | you cannot read it, which is deliberately indistinguishable from a resource that does not exist |
So a 404 on something a colleague can open means it is not shared with any team of yours — ask its owning team for a grant.
From the CLI¶
datavo teams list # your teams + owners of what you can read
datavo teams find-person peter@example.org # their personal team, to share with them
datavo share dataset voice_train_v1 --to qc --level read
datavo share dataset voice_train_v1 --list
datavo unshare dataset voice_train_v1 --to qc
What you learned¶
| Role | reader or contributor: what kind of call you may make |
| Owning team | one per store / dataset / split set, set at creation |
| Where it lands | your personal team, unless you name one at creation |
| Grant | one resource, one team, read or write |
| Publish | a read grant to everyone; withdraw revokes it |
| Dataset read | sufficient for its samples; source access is checked when you build |
| Identity token | you, logged in — it never goes into a program |
| Run token | a program acting as you: never shares, creates into one team, deletes only its own run's creations, lasts at most 14 days |
| 401 / 403 / 404 | the token / what the message names / no read access |
Next: 16. Shard caching and the cache hierarchy — why the second read of a dataset is free.