Audience Labels
Audience labels let a single assistant serve several groups from the same project while keeping each group’s knowledge private to that group.
The classic case: you ingest documents through the API on behalf of a customer whose source system (an LMS, an intranet, a document store) had per-user permissions. Once the content is in Ragtime, those permissions are gone. Audience labels restore a coarse version of them.
The model
Section titled “The model”- A library is the permission boundary. There are no per-document or per-chunk ACLs — if a visitor can retrieve from a library, they can retrieve anything in it.
- Each link between a project and a library carries a list of audience labels. An empty list means “unrestricted”.
- A visitor carries a set of verified labels, derived server-side.
- Retrieval returns a library only if it is unrestricted, or if the visitor’s labels overlap the library’s labels.
Because the filter is applied inside the retrieval query rather than after it, restricted content never enters the model’s context, never appears in citations, and never leaks through diagnostics.
Where labels come from
Section titled “Where labels come from”Visitor labels are only ever derived from a source Ragtime can verify. They are never read from a request body, query string, or header — a caller-supplied label would be a trivial authorization bypass.
Today the only trusted source is an LTI 1.3 launch, whose
id_token signature is verified against the LMS’s public keys before the launch
is accepted. Trust is also opt-in per device, so attaching an LTI launch to an
existing device can never silently widen what it retrieves.
Set Audience source on the device (Dashboard → Deployment → Devices):
| Setting | Effect |
|---|---|
None |
Visitors carry no labels. Only unrestricted libraries are searchable. |
LTI launch |
Labels are derived from the verified launch. |
An LTI launch produces two kinds of label:
lti:context:<contextId> # the course / context the learner launched fromlti:role:<role> # instructor, learner, contentdeveloper, …Roles are normalised: the full IMS role URI
http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor and the short form
Instructor both collapse to lti:role:instructor.
Restricting a library
Section titled “Restricting a library”Go to Dashboard → Settings → Access, find the linked library, and enter comma-separated labels in the Audience column. Leave it empty to keep the library unrestricted.
lti:context:course-4821, lti:context:course-4822That library is now retrievable only by visitors launching from one of those two courses. To make a library instructor-only regardless of course:
lti:role:instructorLabels are lowercased and de-duplicated on save. A link may hold up to 50 labels.
Managing labels over the API
Section titled “Managing labels over the API”If the groups come from a system that already knows them, you probably do not
want to maintain labels by hand. Two scopes cover this, and they are deliberately
separate from ingest: a compromised ingestion connector must not be able to
clear a library’s labels and expose it to everyone.
| Scope | Grants |
|---|---|
audience:read |
Read a library’s labels |
audience:write |
Replace a library’s labels |
Read the current labels:
curl https://your-ragtime-host/api/v1/projects/{projectId}/libraries/{libraryId}/audience \ -H "Authorization: Bearer rt_live_..."{ "libraryId": "…", "audience": ["lti:context:course-4821"], "restricted": true}Replace them. PUT overwrites the whole list rather than merging, so send the
complete set every time:
curl -X PUT https://your-ragtime-host/api/v1/projects/{projectId}/libraries/{libraryId}/audience \ -H "Authorization: Bearer rt_live_..." \ -H "Content-Type: application/json" \ -d '{"audience": ["lti:context:course-4821", "lti:context:course-4822"]}'Sending {"audience": []} removes the restriction and makes the library visible
to every visitor of the project. This is a widening operation and is recorded in
the audit log along with the previous value.
Labels over the limits are rejected, not silently dropped — a caller that believes it restricted a library but did not is the exact failure this feature exists to prevent.
audience:read also adds audience and restricted to each entry returned by
GET /api/v1/projects/{projectId}/libraries, which is the easiest way for a sync
job to reconcile state in one call. Keys without that scope see the list without
them, since label names can themselves reveal cohort names.
Behaviour you should expect
Section titled “Behaviour you should expect”- Existing projects are unaffected. Every pre-existing link has an empty audience, which means unrestricted.
- No labels means unrestricted-only, not deny-all. A visitor with no verified labels still reaches every library you have not restricted.
- Organization members bypass the filter. Dashboard preview and simulation run as a member of the organization and see everything, which is what makes testing possible. Verify isolation from a real launch, not from Preview.
- A restricted library is invisible, not empty. It does not appear in retrieval diagnostics either, so its name cannot be inferred.
- Assistant topics follow the same rule. Topics from restricted libraries are excluded from the compiled system prompt, because that prompt is shared across every visitor to the assistant.
Common misconfiguration
Section titled “Common misconfiguration”If you label a library but leave the device’s Audience source on None, that
library becomes unreachable by everyone — no visitor can produce a matching
label. The dashboard warns about this, but it is the most likely mistake when
first setting things up.
What this is not
Section titled “What this is not”Audience labels are a coarse partition, deliberately. They do not reproduce per-user document permissions from a source system, and they do not attempt to track individual users. If two people belong to the same course, they see the same knowledge. Model your libraries around the groups that genuinely need different answers, not around individual users.
If you need a fully separate persona, prompt, or model as well as separate knowledge, use a separate project instead.
Related
Section titled “Related”- LTI 1.3 (LMS) — how launches are verified.
- API Keys — restricting a key to a single library.
- Ingest API — pushing content into a specific library.