Skip to content

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:

me = client.get_auth_status()
[team.id for team in me.teams]           # your teams, `everyone` included

Name a team explicitly when you want the work to belong to the team rather than to you:

client.create_store(name="voice_v2", team="speech-research")

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:

client.grant_resource_access("dataset", "voice_train_v1", team="qc", level="read")

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:

client.revoke_resource_access("dataset", "voice_train_v1", team="qc")

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:

program = DatavoClient(auth_token=token)

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:

from datavo_sdk import revoke_run_token

revoke_run_token(token)

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.