Bouwen voor personen¶
Het concept achter sleutels en policy staat in Sleutels, capabilities en policy.
Gebruik een subject wanneer meerdere personen je applicatie via dezelfde API-sleutel gebruiken. Een subject bevat een id dat jouw applicatie kiest en optionele attributen, zoals een afdeling. Core gebruikt die gegevens om toegang per persoon te controleren.
Met een subject beperk je toegang tot de gesprekken, het geheugen en de bestanden van die persoon. Daarnaast kun je regels instellen voor toegang tot modellen, tools, prompts, skills, agents, collecties en indexen. Die regels vergelijken de attributen van de persoon met de labels op een resource.
Core beheert geen gebruikersaccounts voor deze personen. Je applicatie levert de identiteit aan; Core registreert het subject-id bij de bijbehorende activiteit. Attributen worden per verzoek beoordeeld; een achtergrondrun bewaart ze tijdelijk versleuteld om later verder te kunnen. Voor een applicatie zonder gedeelde persoonsgegevens kunnen de rechten van de API-sleutel voldoende zijn.
Een subject meesturen op een beurt¶
De persoon reist in de header X-Core-Subject, op elke route die gegevens van een persoon leest of
schrijft: beurten, gesprekken, geheugen, bestanden, artefacten, kennisbronnen en zoeken,
datastore-records, het lezen van modellen, tools, prompts, skills, agents, collecties en
indexen, en het schrijven van agents, prompts en workflows. De API-referentie noemt de header precies op de routes die hem
lezen:
curl -s -X POST "$CORE_URL/responses" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-H 'X-Core-Subject: {"id": "anna", "attributes": {"department": ["payroll"], "office": ["jansen"]}}' \
-d '{"model": "<een model-id>",
"input": "wat is er gebeurd met de loonaangifte van De Vries vorige maand?"}'
idis je eigen ondoorzichtige identificatie voor de persoon -- nooit een naam of adres. De grammatica is dezelfde als OpenAI's eigenuser-veld: letters, cijfers,. _ : @ -, 1 tot 128 tekens.attributesis wat dit verzoek over diegene beweert: maximaal 32 namen, elk met maximaal 32 waarden van maximaal 256 tekens. Eén losse string ("department": "payroll") telt als één waarde. Core bewaart hier niets van, nergens -- niet op de beurt, niet in een auditregel. Stop je met een attribuut meesturen, dan is het bereik dat het kocht bij de eerstvolgende aanroep alweer weg. De enige uitzondering is een beurt die op de achtergrond doorloopt: die houdt de attributen van het verzoek versleuteld bij zich zolang de run duurt, zodat een tweede ronde tegen dezelfde bewering wordt afgewogen als de eerste, en raakt ze kwijt zodra de run klaar is.
Een agent, een prompt of een workflow wijzigen wordt beslist voor de persoon die je noemt.
Er een aanmaken, wijzigen of verwijderen, of er een nieuwe versie van maken (POST /agents, POST
/agents/{id}/versions, DELETE /agents/{id}, dezelfde drie onder /prompts, en POST
/workflows, PATCH en DELETE /workflows/{id}, POST /workflows/{id}/versions), leest de
header. Je regels over write en delete van die soort beslissen voor haar (Zien, gebruiken en
wijzigen scheiden), en het auditspoor van de schrijfactie noemt haar naast de sleutel.
Zonder de header is de schrijfactie van je backend en wordt die beslist zoals altijd, behalve onder
een policy van sleutel of organisatie die settings.subjects_required zet: daar is het 422, zoals op
elke route die de header leest (hieronder). Een
subject-token wijzigt de bibliotheek nooit, wie het ook noemt (hieronder).
Sleutels beheren (/keys), indelen (PUT/DELETE /labels/...) en elke andere route waarbij de
referentie geen X-Core-Subject noemt, negeren de header. Bij een workflowrun (POST
/workflows/{id}/runs) is de persoon de subject_id in de body; de header wordt daar niet gelezen.
Er zijn drie manieren om een persoon te noemen, in aflopende volgorde van gezag:
- Een subject-token dat je backend heeft aangevraagd. Het bindt elk verzoek waarop het wordt
aangeboden aan één persoon; een andere noemen is
403(hieronder). X-Core-Subject, met de JSON hierboven of met een token dat je eigen identity provider heeft ondertekend (hieronder). Het eerste teken bepaalt wat het is:{is de JSON, drie stukken gescheiden door punten is een token.user, het veld dat OpenAI's eigen clients al versturen. Het is een alias van het id in de header en draagt geen attributen. Beide met een andere waarde noemen is400, metheader.X-Core-Subjectinerror.param.
De body is die van OpenAI. Er is geen veld subject in de body. Een body die het meestuurt,
krijgt de weigering die elk onbekend veld krijgt: 400 op POST /responses, 422 op
POST /threads, POST /memory en de twee kennis-zoekroutes. Zet in plaats daarvan de header op je
client; zie Het
verzoekformaat is dat van OpenAI.
Een token van je identity provider¶
Authenticeert je applicatie haar mensen al, dan heb je van je eigen identity provider een token dat
zegt wie iemand is en waar diegene bij hoort. Dat hoef je niet over te typen in JSON: geef het token
door in X-Core-Subject en laat Core de handtekening van je provider controleren. Core leest er de
persoon en zijn attributen uit, bewaart het token niet en bewaart geen enkele claim.
De provider registreren¶
Core gelooft alleen providers die je organisatie heeft geregistreerd -- maximaal acht. In de console
staat de kaart Identity providers op Instellingen van de organisatie
(Een organisatie). Vanuit de API doet een
sleutel met management hetzelfde met PUT /organisation/subject-issuers, die de hele lijst
vervangt:
curl -s -X PUT "$CORE_URL/organisation/subject-issuers" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{"issuers": [{
"issuer": "https://login.jansen.example/",
"audience": "artific-core",
"algorithms": ["RS256"],
"jwks": {"keys": [{"kty": "RSA", "kid": "k1", "n": "...", "e": "AQAB"}]},
"claims": {"id": "sub", "attributes": {"department": "groups"}}
}]}'
| Veld | Wat je invult |
|---|---|
issuer |
precies de iss die een token van deze provider draagt. Exact vergeleken, nooit op prefix |
audience |
precies de aud waarvoor je je eigen tokens laat maken. Verplicht: een token voor een andere applicatie bij dezelfde provider is niet van jou |
algorithms |
RS256, ES256 of allebei; standaard ["RS256"]. Hier vastgelegd, nooit uit de header van het token gelezen |
jwks |
de publieke sleutels van je provider, hier ingeplakt. Dit is wat we aanraden: Core hoeft er geen netwerk voor op. Een JWKS met een privé-helft erin wordt geweigerd |
jwks_uri |
het https-adres van diezelfde sleutels, als alternatief voor jwks. De host moet bij de toegestane hosts van je organisatie staan en mag niet door de installatie zijn geblokkeerd; sta hem toe voordat je het adres invult |
claims.id |
de claim met de eigen identificatie van de persoon; standaard sub. De waarde moet aan dezelfde grammatica voldoen als een id dat je zelf meestuurt |
claims.attributes |
per attribuut de claim waar het uit komt: maximaal 32. Een pad met punten leest een geneste claim (org.unit), en een claim die zelf punten in zijn naam heeft (https://jansen.example/department) wordt eerst als geheel gezocht |
Bij Jansen zet {"department": "groups"} de "groups": ["payroll"] van de provider om in Anna's
department: [payroll]. De regel die je over department had geschreven beslist daarna op wat de
identity provider zei in plaats van op wat de applicatie typte.
Een claim die het token niet draagt, is een attribuut dat dit verzoek niet beweert: geen fout, wel
minder bereik. Een claim die er wél is maar geen string of lijst van strings is, wordt geweigerd --
5 lezen als "5" zou één regel op twee manieren laten beslissen.
Wat een weigering zegt¶
Elk token dat niet door de controle komt levert dezelfde weigering op, over het veld
header.X-Core-Subject. Op POST /responses is dat een 400 in het foutobject van
OpenAI:
{
"error": {
"message": "X-Core-Subject must be a JSON object naming a person, or a token an issuer of this organisation signed.",
"type": "invalid_request_error",
"param": "header.X-Core-Subject",
"code": "invalid_request"
}
}
POST /messages geeft dezelfde zin in de envelop van Anthropic. Elke andere route antwoordt met
422 problem+json, met het veld in errors.
Eén zin, altijd dezelfde: voor een verlopen token, een handtekening die niet klopt, een aud die
niet die van jou is, en een provider die je nooit hebt geregistreerd. Dat is opzet. Wie alleen een
sleutel heeft, mag met een reeks probeersels niet kunnen uitvinden hoe jouw identiteiten zijn
ingericht. Zoek de oorzaak dus bij je provider en bij de registratie hierboven, niet in het
antwoord.
Eén geval antwoordt anders: kan Core de sleutels op je jwks_uri niet ophalen, dan is het 503 met
The identity provider's keys could not be reached. Die mag je opnieuw proberen; de weigering
hierboven niet.
Een token voor de browser¶
Praat een browser of mobiele app rechtstreeks met Core -- een gestreamde beurt, een upload -- dan mag die nooit je sleutel dragen: die geldt voor iedereen. Je backend vraagt daarom een subject-token aan, je sleutel versmald tot één persoon, en geeft dat aan het apparaat:
curl -s -X POST "$CORE_URL/subjects/tokens" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{"subject": {"id": "anna", "attributes": {"department": ["payroll"]}}, "expires_in": 900}'
{"token": "ast_...", "subject_id": "anna", "expires_at": "2026-09-20T11:15:00Z"}
Heb je een token van je identity provider, stuur dan assertion in plaats van subject, met dat
token als waarde -- precies één van de twee, allebei of geen van beide is 422. De persoon en de
attributen zijn dan het woord van je provider in plaats van dat van je backend; verder verandert er
niets.
Een token bevat hoogstens 4096 tekens. Het apparaat stuurt het mee als header, dus Core weigert
een token te maken dat langer zou worden. Dat zijn ongeveer twintig attributen van gewone lengte;
het precieze aantal hangt af van hoe lang je namen en waarden zijn, dus vooraf tellen kan niet. Een
aanvraag boven de grens antwoordt 422 met subject.attributes als veld in errors en de melding
The attributes are too large to bind into a token; assert fewer or shorter values. Zet in een
token alleen de attributen die je regels lezen. Een subject buiten de algemene grenzen (32 namen, 32
waarden per naam, 256 tekens per waarde) wordt eerder geweigerd, met 422 op het veld dat niet
klopt onder body.subject.attributes.
Het apparaat stuurt het token mee als Authorization: Bearer ast_... en noemt zelf niemand: een
verzoek dat een andere persoon noemt is 403. Verder geldt:
- Het leeft
expires_inseconden, standaard 900 en hoogstens 3600. Core houdt geen lijst bij van de tokens die het maakte, dus één los token intrekken kan niet. Drie dingen beëindigen elk token dat een sleutel ooit maakte, in één keer: die sleutel roteren, hem verwijderen, en het verstrijken van de eigenexpires_atvan de sleutel. Een beëindigd token antwoordt401metWWW-Authenticate: Bearer, hetzelfde antwoord als een onbekende sleutel. - Het bereikt de gegevens van die persoon en leest de bibliotheek van je organisatie -- modellen,
prompts, skills, agents, tools, collecties -- zodat je applicatie kan tonen waaruit iemand kan
kiezen. Beheren doet het niets: elke schrijfactie op de bibliotheek,
GET /key, de routes onder/subjectsen alles onder beheer antwoorden403met A subject token may not reach this route. Doe die aanroepen vanuit je backend. - Het wijzigt alleen rijen die voor zijn eigen persoon zijn geschreven. Elke andere rij die het
kan lezen wijzigen -- een gesprek dat niemand in het bijzonder opende, een feit dat de organisatie
zelf schreef, een bestand dat je backend uploadde -- antwoordt
403met A subject token may not reach this route. Dat geldt voor bewerken en verwijderen, voor iets toevoegen aan zo'n gesprek en voor het voortzetten ervan met een beurt. Een rij die het token helemaal niet kan lezen, antwoordt404, net als bij een sleutel. - Het leest alleen de traces van de aanroepen van zijn eigen persoon; elke andere trace antwoordt
404. - Core slaat het token nergens op, dus je krijgt het één keer te zien. De aanroep zit achter de
capability
subjects.
Core aanroepen vanuit een webpagina. De gateway beantwoordt cross-origin-verzoeken van elke
origin, dus je hoeft geen domein te registreren. Credentials staat hij nooit toe: de browser stuurt
geen cookies naar Core, en je pagina stuurt het token zelf mee in de header Authorization: Bearer.
De preflight van de browser -- een OPTIONS-verzoek onder /api/v1 met Origin en
Access-Control-Request-Method -- beantwoordt de gateway zelf, en de browser mag dat antwoord 600
seconden hergebruiken. Elke methode en elke requestheader die de API kent is toegestaan,
X-Core-Subject inbegrepen. Je pagina kan deze responseheaders lezen: Content-Type, Location,
Retry-After, X-Request-Id, Deprecation, Sunset, X-RateLimit-Limit,
X-RateLimit-Remaining en X-RateLimit-Reset.
Een sleutel die een browser stuurt, wordt geweigerd. Een verzoek met het geheim van je sleutel
dat een browser verstuurt, krijgt 403 met reason_code key_in_browser, op elke route en vanaf
elke pagina behalve de eigen gids van deze installatie, waarvan de
verkenner je sleutel aanneemt: iedereen die de pagina opent, zou
de sleutel kunnen lezen en namens je hele organisatie handelen. Core herkent een browser aan de headers Origin en Sec-Fetch-Site, die browsers
toevoegen en die een pagina niet kan weghalen. Je backend blijft met de sleutel werken: curl, Node,
Python, de SDK's van OpenAI en Anthropic en andere clients op een server sturen geen van beide. Een
backend die het verzoek van een browser doorgeeft aan Core, stuurt die twee headers niet mee.
X-Api-Key is voor een browser helemaal geen toegestane header, dus de preflight ervan mislukt
voordat de sleutel wordt verstuurd.
Bronnen labelen bij upload¶
De attributen van een subject reizen mee met het verzoek; de labels van een bron staan erop
opgeslagen. Voeg labels toe bij het aanmaken of uploaden van een kennisbron:
curl -s -X POST "$CORE_URL/knowledge/collections/col_2Pf7.../sources" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{"kind": "text", "title": "Loondossier De Vries",
"text": "...",
"labels": {"department": "payroll", "client": "devries"}}'
Een regel leest deze labels om te beslissen voor wie een beurt de bron mag terugvinden. metadata
blijft van jou, voor de zoekfilters die Kennis
beschrijft; één release lang leest een regel het nog waar labels de naam missen
(Labels en metadata).
Labels op alles¶
Een regel kan een label lezen op vijftien soorten dingen, en voor elke soort geldt dezelfde vraag:
welk label draagt het ding dat de beurt pakt? De namen hieronder zijn de kind waarmee je een regel
schrijft (Je eerste regel schrijven).
| Soort | Waar het label staat |
|---|---|
thread, memory_record, knowledge_source, stored_file, datastore_record |
het veld labels van de rij zelf, zoals hierboven, met dezelfde waarden als bij een bibliotheekrij; bij datastore_record de labels van één record |
prompt, skill, agent, schedule, workflow, knowledge_collection, knowledge_index, knowledge_graph, en als tool alles wat een tool oplevert -- API-koppelingen, SQL-koppelingen, scripts, MCP-servers, toolsets, tool-aliassen, zoektools |
het veld labels van die rij: maximaal 32 labels, elk met één waarde of een lijst van 1 tot 32 verschillende waarden; een waarde is een string van 1 tot 256 tekens |
model, en een tool zonder eigen rij: een ingebouwde tool (builtin__*), een operatie van Core's eigen API (core__*) en de uploadtool |
een aparte route, PUT /labels/model/{id} en PUT /labels/tool/{naam}, vanaf een sleutel of vanuit de console; het label van de uploadtool geldt voor elke sleutel van de organisatie |
curl -s -X PUT "$CORE_URL/labels/tool/builtin__web_search" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{"labels": {"audience": "everyone"}}'
Een PUT vervangt de hele indeling van dat ene ding; GET /labels?kind=tool toont wat er staat en
DELETE haalt het weg. Een tool die wél een eigen rij heeft, wordt daar ingedeeld en niet hier: die
naam antwoordt 422.
Een label in een veld labels mag meerdere waarden hebben. Schrijf een lijst als één waarde
niet genoeg is: een prompt voor twee afdelingen draagt {"department": ["sales", "finance"]}, en
een regel over department: sales bereikt hem net zo goed als een regel over
department: finance. Hetzelfde geldt voor PUT /labels/model/{id} en PUT /labels/tool/{naam}.
Zodra je een vocabulaire vastlegt, wordt een lijst alleen aangenomen
onder een label dat multiple is vastgelegd. Waar een regel tijdens de
overstap naar labels nog de metadata
van een rij leest, leest hij één string: een lijst daarin matcht geen enkele regel.
Uploads en artefacten zijn een stored_file, waar je ze ook bereikt. Onder water zijn het
allebei kennisbronnen, maar op alle drie de plekken waar ze verschijnen beslissen de regels over
stored_file: op
GET /files, tussen de bronnen van een collectie, en in wat de zoektool knowledge__uploads vindt.
Onder de standaardregels bereikt een persoon haar eigen uploads en de gedeelde -- geüpload door een
verzoek dat niemand noemde -- en nooit die van een ander. Elke andere soort bron (tekst, records,
wat een connector synchroniseert) is een knowledge_source.
Een tool erft het label van zijn bron. Een API-koppeling, een SQL-koppeling en een script
leveren elk één tool op (api__<naam>, sql__<naam>, script__<naam>), die het label van de bron
draagt; elke tool van een MCP-server draagt dat van de server, en een zoektool dat van haar eigen
rij. Een alias draagt zijn eigen label en wordt daarnaast op zijn doel beoordeeld -- twee namen,
twee indelingen, één aanroep. Een toolset draagt haar eigen label, en elke tool erin wordt nog
steeds apart beoordeeld.
Het voorbeeld van Jansen erbij: de MCP-server salaris -- en dus elke mcp__salaris__*-tool -- krijgt
department: payroll, de ingebouwde builtin__web_search krijgt audience: everyone, de agent
payroll-assistant krijgt department: payroll en het model gpt-5 krijgt tier: premium. Labels
alleen veranderen niets: de standaardregels staan iedereen elke tool, elke agent en elk model toe.
Zodra het beleid van de sleutel regels over tool, agent en model bevat die deze labels
vergelijken met de department en de tier van een persoon (Je eerste regel
schrijven schrijft die voor tool), krijgt Anna
(department: [payroll], geen tier) de mcp__salaris__*-tools en websearch aangeboden, mag ze de
payroll-agent noemen en wordt gpt-5 haar geweigerd voordat de leverancier wordt gebeld. Bob van de
receptie krijgt alleen websearch aangeboden en hoort dat de payroll-agent niet bestaat.
Twee vuistregels houden dit werkbaar:
- Een label is een indeling, nooit het id of een attribuutwaarde van een persoon.
department: payrollentier: premiumzeggen wat het ding is; wie het verzoek is voor, zeggen de attributen. Zet je iemands id in een label, dan heb je één rij per persoon te onderhouden en staat er in je bibliotheek wie je gebruikers zijn. - Niets invullen is niet hetzelfde als niets toestaan. Een ding zonder label matcht geen
enkele regel die een label test, dus een
permitdie je over een label schrijft laat het weg. Zolang je voor die soort de standaardregels houdt, verandert er niets: die staan elke kennisbron en alles in elke bibliotheek toe.
In de console schrijf je labels op het scherm van de soort zelf: een veld Labels in het
formulier, en een kolom Labels in de lijst (Prompts,
Modellen, en zo voor elke bibliotheek). Wil je een label meerdere
waarden geven, voeg dan per waarde een rij toe onder dezelfde naam, zoals department: sales en
department: finance; de lijst toont elke waarde als een eigen badge.
Welke labels een persoon mag schrijven¶
Een regel die een thread met project: apollo laat zien aan iedereen van Apollo, is zo goed als
het label. De labels van een rij schrijft het verzoek zelf, dus zonder meer kan een persoon haar eigen thread
labelen met een project waar ze niet in zit, en hem zo aan elk lid van dat project geven. Dat sluit
je met writable_labels in de settings van het beleid: per soort rij wijst elke labelnaam naar
waar een waarde onder die naam aan moet voldoen. Dat is "subject" voor haar eigen id,
{"attribute": "projects"} voor een van haar waarden voor dat attribuut, of {"value": "open"}
voor één vaste string.
De regel die het gesprek aan het project laat zien, komt in de rules van het beleid:
{"id": "threads-of-my-projects", "effect": "permit", "kind": "thread",
"when": [{"atom": "label_among", "label": "project", "attribute": "projects"}]}
en de map die het label bewaakt in de settings:
{"writable_labels": {"thread": {"project": {"attribute": "projects"}, "assignee": "subject"}}}
Voeg beide toe aan het document van de sleutel en stuur het in zijn geheel terug (Je eerste regel schrijven laat zien hoe).
Anna (projects: [apollo]) mag nu project: apollo en assignee: anna op een thread schrijven,
en project: mercury of assignee: bob antwoordt 422 met het veld erbij (labels.project),
zonder dat er iets wordt opgeslagen. Op POST /responses, waar het opgeslagen antwoord de labels
van het verzoek draagt (in een bestaand gesprek die van het gesprek plus die van het verzoek, en
alleen wat je toevoegt of wijzigt valt eronder; zie
overerving), is de weigering de eigen 400 van de leverancier met error.param op
labels.project -- ook met store: false, omdat de rij van het antwoord zijn labels hoe dan ook
draagt. Eén release lang valt een waarde onder dezelfde naam in metadata, waar labels die
missen, onder dezelfde map en wordt die geweigerd op metadata.project. Waar het voor geldt:
- Elk verzoek dat een persoon noemt -- een subject-token, en een sleutel die iemand noemt in
X-Core-Subjectofuser. Een sleutel die niemand noemt schrijft elk label, zoals altijd. - De vijf soorten rij:
thread(een thread, en delabelsvan een opgeslagen antwoord),memory_record,knowledge_source,stored_fileendatastore_record, elk met een eigen map. Een bron valt onder de map van de soort waaronder hij gelezen wordt, dus een upload is eenstored_file, via welke route hij ook is opgeslagen -- en depurposevan een upload viaPOST /filesis zijn labelpurpose, geweigerd op het veldpurpose. Een model dat binnen haar beurt een geheugenrecord of datastore-record schrijft, valt er ook onder en hoort dat als toolresultaat. - Alleen wat verandert. Een patch die een label houdt dat de rij al draagt -- een backend zette het -- wordt niet geweigerd, en een label weghalen mag altijd. Hetzelfde geldt voor een beurt in een gelabeld gesprek: de labels die ze erft blijven staan en worden niet gewogen.
- Een naam die de map niet noemt is vrij. Wie geen waarde voor het attribuut meestuurt -- een
bezoeker zonder
projects-- schrijft geen enkele waarde voor de namen die ernaar wijzen.
De policy van je organisatie en die van je sleutel worden allebei gevraagd; een waarde wordt alleen
geschreven als geen van beide hem weigert. Met een vocabulaire moet elke
genoemde naam een label zijn dat voor die soort is vastgelegd en elk attribuut er een dat een
verzoek mag meesturen, anders antwoordt het schrijven van de policy 422 op
settings.writable_labels.<soort>.<naam>.
Leg je vocabulaire vast¶
Zonder vocabulaire mag elke naam: elke naam in een labels-map, elk woord kan
als attribuut worden beweerd, en elke naam kan in een regel staan. Een typefout is daardoor geen
fout: departement in een regel wordt zonder klagen opgeslagen en matcht niets, dept in een header
beweert een attribuut waar geen regel naar kijkt, en visibility: publik op een dossier verbergt
het voor iedereen. Stuk voor stuk stil, en stuk voor stuk dicht -- precies de richting die
eruitziet alsof het werkt.
Leg daarom eerst vast welke namen je organisatie gebruikt:
curl -s -X PUT "$CORE_URL/organisation/label-vocabulary" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{"labels": {
"department": {"values": ["payroll", "reception"],
"kinds": ["knowledge_source", "tool", "agent"],
"multiple": true,
"attribute": true,
"description": "de afdeling waar iets bij hoort"},
"visibility": {"values": ["public", "internal"], "kinds": ["knowledge_source"]},
"client": {"values": null, "kinds": ["knowledge_source"]}
}}'
Per label leg je tot vijf dingen vast. values is de gesloten verzameling waarden, of null voor
"elke string" -- dat laatste is wat je wilt voor iets als een klantnaam. kinds zegt welke van de
vijftien soorten dit label dragen, dus een label voor kennisbronnen kan niet op een prompt worden gezet of in
een regel over threads worden gelezen. Met multiple: true mag een veld labels onder deze naam een
lijst waarden hebben, zoals een tool die bij zowel payroll als reception hoort; zonder wordt een
lijst geweigerd op labels.<naam>. De metadata van een rij wordt nooit tegen de vocabulaire
gecontroleerd. attribute: true is de aparte toestemming om de naam over
een persoon te beweren; een label dat alleen op rijen staat, laat je op false. description
is de zin die de console naast de naam toont.
Vanaf dat moment wordt een naam geweigerd zodra hij niet in het vocabulaire staat -- bij het schrijven, met de naam erbij:
- een regel over
departementwordt geweigerd bij het opslaan van het beleid, met het pad van het onderdeel dat de naam noemt, zoalsrules[3].when[0].label; - een prompt met
labels: {"zonne": "noord"}wordt geweigerd oplabels.zonne; - een verzoek met
X-Core-Subject: {"id": "anna", "attributes": {"dept": "payroll"}}wordt geweigerd opheader.X-Core-Subject--400op een modelroute,422op elke andere.
Labels en regels worden gecontroleerd op het moment dat je ze schrijft en niet opnieuw tijdens een
beurt; de subjectheader is het enige dat bij elk verzoek tegen het vocabulaire wordt gehouden. Elke
sleutel van de organisatie mag het vocabulaire lezen met GET /organisation/label-vocabulary --
schrijven vraagt management.
Leg het vast voordat je regels en labels schrijft. Heb je al iets staan dat er niet in past,
dan wordt de PUT geweigerd met een lijst conflicts van precies die rijen, en wordt er niets
opgeslagen: ruim ze eerst op. Heb je geen vocabulaire, dan blijft alles zoals het was -- elke naam
mag -- en DELETE /organisation/label-vocabulary brengt je daar weer terug.
Je eerste regel schrijven¶
Een beleidsdocument bestaat uit twee delen: rules, een lijst, en settings. De regels over
personen staan in dezelfde lijst als elke andere regel waaronder je sleutel valt: welke modellen hij
mag aanroepen, welke tools, welke hosts. Wijzig ze in de console, op de eigen pagina van de
sleutel (Sleutels), of met PATCH /keys/{key_id} als je sleutel
management heeft.
Een regel heeft deze onderdelen:
| Onderdeel | Wat het zegt |
|---|---|
effect |
permit (toestaan) of forbid (verbieden) |
kind |
de soort ding waar de regel over gaat (de tabel hieronder) |
match.names |
patronen waarvan de naam van het ding er één moet matchen: *, ? en [a-z], hoofdlettergevoelig. Laat het weg voor elke naam |
match.labels |
labels die het ding moet dragen, elk met deze waarde onder zijn waarden. subject is de eigenaar van de rij, en "" daarop is een rij die niemand in het bijzonder heeft gemaakt |
when |
voorwaarden aan de persoon die het verzoek noemt; ze moeten allemaal gelden |
actions |
optioneel. De acties waar de regel over gaat: see, use, write, delete (hieronder). Laat het weg voor elke actie die de soort neemt |
id |
optioneel. Een eigen naam voor de regel: kleine letters, cijfers en koppeltekens, beginnend met een letter, uniek in het document. Het auditspoor noemt de regel die besliste bij deze naam |
Een voorwaarde in when is een van vier:
| Voorwaarde | Geldt als |
|---|---|
{"atom": "nobody_named"} |
het verzoek geen persoon noemt |
{"atom": "label_is_subject", "label": "assignee"} |
de eigen id van de persoon onder de waarden van het ding voor assignee staat |
{"atom": "label_among", "label": "department", "attribute": "department"} |
minstens één waarde van het ding voor department ook een van de waarden van de persoon voor het attribuut department is |
{"atom": "attribute_has", "attribute": "role", "value": "admin"} |
de waarden van de persoon voor het attribuut role admin bevatten. Op het ding leest deze niets |
Met een vocabulaire moet de waarde waar attribute_has om vraagt in de
gesloten verzameling van dat label staan, anders antwoordt het schrijven van het beleid 422 op
rules[<i>].when[<j>].value.
Drie feiten beslissen elk geval:
- Een
forbiddie matcht weigert, wat eenpermitook zegt. - Niets is toegestaan zonder een
permitdie matcht. De volgorde van de regels doet er niet toe. - Een regel geldt alleen als zijn hele
whengeldt. Een voorwaarde die de persoon leest, geldt nooit voor een verzoek dat niemand noemt.
Een sleutel die zonder beleid is geregistreerd, krijgt de standaardregels, uitgeschreven in zijn document:
| Soort | Een regel van deze soort beslist | De standaardregels staan toe |
|---|---|---|
model, router |
welke modellen en modelrouters een beurt mag gebruiken | allemaal |
tool |
welke tools het model krijgt aangeboden en mag aanroepen | allemaal |
prompt, skill, agent, schedule, workflow, file, knowledge_collection, knowledge_index, knowledge_graph, datastore |
welke bibliotheekitems een verzoek op naam bereikt | allemaal |
knowledge_source |
welke kennisbronnen een zoekopdracht of een leesactie bereikt | allemaal, omdat de labels erop aan jou zijn om te bepalen en Core ze niet kan raden |
thread, memory_record, stored_file, datastore_record |
welke rijen een verzoek bereikt | elke rij als het verzoek niemand noemt; de eigen rijen van een persoon plus de rijen die niemand in het bijzonder heeft gemaakt als het er een noemt |
egress_host |
welke hosts de sleutel mag bereiken, binnen de toegestane hosts van de organisatie | geen regel: de sleutel bereikt de hosts van de organisatie |
artifact_download |
of bestanden die een tool maakte gedownload mogen worden | ja |
memory_write |
of een beurt geheugen mag schrijven | nee |
direct_turn |
of een beurt die geen agent noemt mag draaien: een gesprek met een kaal model | geen regel, en dat staat elke zulke beurt toe (hieronder) |
Om kennisbronnen en tools te versmallen voor het voorbeeld van Anna, lees je het document van de
sleutel met GET /key, haal je de twee standaardregels met de id's knowledge-source en tool uit
rules, en zet je deze zes ervoor in de plaats:
[
{"id": "sources-nobody-named", "effect": "permit", "kind": "knowledge_source",
"when": [{"atom": "nobody_named"}]},
{"id": "sources-own-department", "effect": "permit", "kind": "knowledge_source",
"when": [{"atom": "label_among", "label": "department", "attribute": "department"}]},
{"id": "sources-public", "effect": "permit", "kind": "knowledge_source",
"match": {"labels": {"visibility": "public"}}},
{"id": "tools-nobody-named", "effect": "permit", "kind": "tool",
"when": [{"atom": "nobody_named"}]},
{"id": "tools-own-department", "effect": "permit", "kind": "tool",
"when": [{"atom": "label_among", "label": "department", "attribute": "department"}]},
{"id": "tools-for-everyone", "effect": "permit", "kind": "tool",
"match": {"labels": {"audience": "everyone"}}}
]
Stuur het document daarna in zijn geheel terug:
curl -s -X PATCH "$CORE_URL/keys/key_9Nx4..." \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d @policy.json
waarbij policy.json {"policy": {"rules": [...], "settings": {...}}} bevat met de aangepaste
lijst. Anna bereikt een bron of een tool die is gelabeld met haar eigen department, plus alles wat
voor iedereen is; een verzoek dat niemand noemt bereikt nog steeds elke bron en elke tool.
Een document is precies de regels die erin staan. PATCH vervangt het hele beleidsdocument: een
document zonder rules krijgt de standaardregels, en een document met "rules": [] staat niets
toe, wat een geldige stand is. Toestemmingen tellen op, dus wie wil dat iemand naast haar eigen
gesprekken ook die van haar afdeling ziet, houdt de drie standaardregels voor thread en voegt een
vierde toe. Een document bevat maximaal 500 regels; een regel maximaal 200 naampatronen, 16
labeltests en 16 voorwaarden.
Niet elke soort neemt elk onderdeel. Een rij heeft geen naam, dus match.names wordt geweigerd
op thread, memory_record, knowledge_source, stored_file en datastore_record, en op
artifact_download en memory_write. match.labels wordt geweigerd op router, egress_host,
file, datastore, artifact_download en memory_write, waarvan de dingen geen labels dragen.
when wordt geweigerd op router, egress_host, artifact_download en memory_write, waarbij
nooit naar een persoon wordt gevraagd, en file en datastore nemen alleen nobody_named en
attribute_has, de twee voorwaarden die geen label lezen: over de eigen rijen van de persoon wordt
beslist op de rij, als stored_file en datastore_record. direct_turn neemt alleen when, met
diezelfde twee voorwaarden, en alleen forbid. Een onderdeel dat een
soort niet neemt, antwoordt 422.
Gesprekken, opgeslagen beurten en geheugen zijn van je organisatie, niet van de sleutel die ze
schreef. De regels beslissen welke ervan een persoon bereikt, welke sleutel ze ook aanmaakte. Een
subject-token van de ene sleutel bereikt dus een gesprek dat een andere sleutel van je organisatie
voor dezelfde persoon opende, onder de regels van de sleutel waarmee het token is gemaakt. Bij
bestanden werkt één ding anders: GET /files zonder filter toont alleen de uploads in de eigen
collectie van de aanroepende sleutel. Lezen op id is niet zo beperkt. Het token van een andere
sleutel, of een andere sleutel die dezelfde persoon noemt, leest het bestand op zijn id als haar
regels de collectie van het bestand bereiken en de persoon klopt
(Wat een bestands-id bereikt).
Een sleutel die een persoon noemt, handelt voor haar op elke rij die de regels haar toestaan.
Noem je Anna in X-Core-Subject, dan kan je sleutel iets toevoegen aan, het label wijzigen van,
verwijderen of voortzetten van elk gesprek dat de regels haar laten bereiken, ook een gedeeld
gesprek of, onder een eigen regel, dat van een collega. Het gesprek houdt zijn eigenaar. Het
auditspoor en de nieuwe opgeslagen beurt dragen Anna als persoon; onder de standaardregels krijgt de
eigenaar van het gesprek daarna 404 op GET /responses/{id} voor Annas beurt, terwijl hij de items
nog steeds via het gesprek leest. Alleen een subject-token mag uitsluitend de rijen van zijn eigen
persoon wijzigen (hierboven).
Regels over knowledge_collection en knowledge_index beslissen wat een verzoek op id mag
bereiken, zoals de collectie waarvan het de bronnen opvraagt of de index die het doorzoekt.
Zoekresultaten filteren ze nooit; dat doen de regels over knowledge_source en stored_file.
De regels over knowledge_collection en knowledge_index gelden niet voor knowledge__uploads, de
zoektool over de eigen collectie van je sleutel: een regel van de soort tool die hem noemt, haalt
hem weg.
Het model laten zoeken legt uit hoe elk meetelt als het
model zoekt.
Een geweigerde leesactie ziet er voor de aanroeper uit als afwezigheid: een lijst laat de rij weg,
een id antwoordt 404, een model of een tool die de persoon niet bereikt bestaat niet voor deze
beurt, en een tool wordt niet aan het model aangeboden. Twee weigeringen zijn geen afwezigheid. Een
subject-token dat een rij wijzigt die het kan lezen maar die niet van zijn eigen persoon is,
antwoordt 403 (hierboven). En een actie die de regels niet toestaan op iets wat de aanroeper
mag zien, of een aanmaak of nieuwe labels die ze niet toestaan, antwoordt 403 en verandert niets
(Zien, gebruiken en wijzigen scheiden): POST /threads of PATCH /threads/{id} met
labels die geen write-regel over thread de persoon toestaat, wordt zo geweigerd. De aanroeper
kent dat ding al, of het bestaat nog niet, dus er is niets voor een 404 om te verbergen. Het auditspoor van
je organisatie legt elke weigering vast, met een van twee redenen: forbidden by a rule als een
forbid matchte, en anders no rule permits.
Zien, gebruiken en wijzigen scheiden¶
Een regel kan de acties noemen waar hij over gaat. Zonder acties dekt een regel alles wat een verzoek met een ding kan doen. Met acties kun je bijvoorbeeld iedereen een agent laten gebruiken, terwijl alleen beheerders hem mogen wijzigen.
| Actie | Gevraagd als |
|---|---|
see |
het ding in een lijst staat, of op zijn id wordt gelezen |
use |
een beurt, een run of een aanroep het gebruikt: een agent of prompt die een beurt noemt, een skill die ze koppelt, een tool die aan het model wordt aangeboden of wordt aangeroepen, een model dat wordt aangeroepen, een index die wordt doorzocht, een bestand dat wordt bijgevoegd, een workflow die wordt gestart |
write |
het ding wordt aangemaakt, gewijzigd, hernoemd, opnieuw gelabeld of krijgt een nieuwe versie; bij een gesprek of een andere rij ook als er iets aan wordt toegevoegd |
delete |
het ding wordt verwijderd |
{"id": "agents-everyone", "effect": "permit", "kind": "agent", "actions": ["see", "use"]}
Een soort neemt alleen de acties die er iets voor betekenen:
| Soorten | Acties |
|---|---|
prompt, skill, agent, schedule, workflow, knowledge_collection, knowledge_index, knowledge_graph, datastore, file, tool |
see, use, write, delete |
model |
see, use |
thread, memory_record, knowledge_source, stored_file, datastore_record |
see, write, delete |
router, egress_host, artifact_download, share_link, memory_write, direct_turn |
geen |
Een actie die de soort niet neemt, actions op een soort die er geen neemt, en een lege lijst of
een lijst met een actie twee keer erin antwoorden 422 op de actions van de regel.
- Een regel zonder
actionsdekt elke actie die zijn soort neemt. De standaardregels noemen geen acties, dus een beleid dat je zonder acties schreef beslist precies als voorheen. - Geen actie omvat een andere. Een
permitvoor alleenwritelaat een persoon het ding niet vinden; geef haar ooksee. Core vraagt eerstseeen dan de actie die het verzoek nodig heeft. Een ding dat de persoon niet mag zien antwoordt404. Een ding dat ze ziet maar niet mag gebruiken, wijzigen of verwijderen antwoordt403metpolicy-denied, en het auditspoor legt de regel vast die besliste. - Een tool staat in de lijst door
seeen wordt aangeboden dooruse.GET /toolstoont de tools die een persoon mag zien; een beurt biedt het model alleen de tools aan die ze mag gebruiken. - Een
forbidmetactionsweigert alleen die acties. Deze regel voorkomt dat agents in productie worden verwijderd en verandert verder niets:{"effect": "forbid", "kind": "agent", "actions": ["delete"], "match": {"labels": {"stage": "production"}}}.
Iets aanmaken is een write, beslist voor de persoon en met de labels in de body. Een
permit voor write waarvan de labels passen bij die van het nieuwe ding laat de persoon het
aanmaken; een forbid voor write op een label weigert het aanmaken van iets dat dat label
draagt. Een wijziging van labels wordt beslist op de oude labels en op de nieuwe, dus een persoon
kan een ding niet naar een afdeling verplaatsen, of eruit halen, waarin ze niet mag schrijven. Een
beleid zonder regel over een soort staat daarvan niets toe, dus het weigert een nieuw ding van die
soort net zoals het de bestaande verbergt.
Uitgewerkt voorbeeld: twee afdelingen en beheerders¶
Een organisatie heeft een afdeling Sales en een afdeling Finance, een API-koppeling die beide
gebruiken, en beheerders die agents mogen wijzigen. De applicatie legt eenmalig haar vocabulaire
vast, als body van PUT /organisation/label-vocabulary
(Leg je vocabulaire vast):
{"labels": {
"department": {"values": ["sales", "finance"], "kinds": ["tool", "agent"],
"attribute": true, "multiple": true},
"role": {"values": ["member", "admin"], "kinds": ["agent"], "attribute": true},
"stage": {"values": ["draft", "production"], "kinds": ["agent"], "attribute": false}
}}
Ze labelt haar bibliotheek:
| Ding | Labels |
|---|---|
API-koppeling crm (tool api__crm) |
department: "sales" |
API-koppeling boekhouding (tool api__boekhouding) |
department: "finance" |
API-koppeling agenda (tool api__agenda) |
department: ["sales", "finance"] |
agent offerte |
department: "sales", stage: "production" |
agent facturen |
department: "finance", stage: "draft" |
De sleutel houdt de standaardregels voor elke andere soort. Deze zes regels komen in de plaats van
de standaard-permits met de id's tool en agent:
{"rules": [
{"id": "backend-tools", "effect": "permit", "kind": "tool",
"when": [{"atom": "nobody_named"}]},
{"id": "department-tools", "effect": "permit", "kind": "tool", "actions": ["see", "use"],
"when": [{"atom": "label_among", "label": "department", "attribute": "department"}]},
{"id": "backend-agents", "effect": "permit", "kind": "agent",
"when": [{"atom": "nobody_named"}]},
{"id": "department-agents", "effect": "permit", "kind": "agent", "actions": ["see", "use"],
"when": [{"atom": "label_among", "label": "department", "attribute": "department"}]},
{"id": "admins-change-agents", "effect": "permit", "kind": "agent",
"actions": ["see", "write", "delete"],
"when": [{"atom": "attribute_has", "attribute": "role", "value": "admin"}]},
{"id": "production-stays", "effect": "forbid", "kind": "agent", "actions": ["delete"],
"match": {"labels": {"stage": "production"}}}
]}
admins-change-agents gebruikt de voorwaarde attribute_has: die kijkt alleen naar de persoon, dus
hij geldt voor elke agent zodra de role van het verzoek admin bevat. Elk verzoek noemt zijn
persoon:
X-Core-Subject: {"id": "anna", "attributes": {"department": ["sales"], "role": ["member"]}}
X-Core-Subject: {"id": "bram", "attributes": {"department": ["finance"], "role": ["admin"]}}
| Verzoek | Anna (Sales, member) | Bram (Finance, admin) | Backend, niemand genoemd |
|---|---|---|---|
GET /api_connectors |
crm, agenda |
boekhouding, agenda |
alle drie |
| de tools van een beurt | api__crm, api__agenda |
api__boekhouding, api__agenda |
alle drie |
GET /agents |
offerte |
facturen, offerte: beheerders zien elke agent |
beide |
een beurt die de agent offerte noemt |
toegestaan | 403: hij mag hem zien, maar alleen Sales mag hem gebruiken |
toegestaan |
een nieuwe versie van facturen |
404: ze ziet hem niet |
toegestaan (admins-change-agents) |
toegestaan |
POST /agents met label department: sales |
403: geen regel staat haar write toe |
toegestaan | toegestaan |
DELETE /agents/{agent_id} voor offerte |
403 |
403: production-stays verbiedt het |
403: de forbid geldt voor iedereen |
De koppeling agenda draagt beide afdelingen, dus één regel bereikt hem voor Anna en voor Bram.
Twee varianten hebben geen nieuwe onderdelen nodig. Wil je dat beheerders alleen de agents van hun
eigen afdeling wijzigen, voeg dan de voorwaarde label_among toe aan admins-change-agents: de
voorwaarden in één when moeten allemaal gelden. Wil je dat beheerders ook elke agent gebruiken,
voeg dan use toe aan de acties.
Een persoon niet met een kaal model laten praten¶
Verkoop je abonnementen per persoon, dan wil je misschien dat iemand met een basisabonnement met de
agents van je organisatie praat, haar tools gebruikt en in haar kennis zoekt, maar niet rechtstreeks
met een model chat. Schrijf daarvoor één forbid over de soort direct_turn, een beurt die geen
agent noemt:
{"id": "no-playground", "effect": "forbid", "kind": "direct_turn",
"when": [{"atom": "attribute_has", "attribute": "plan", "value": "basic"}]}
Een beurt voor een persoon van wie plan de waarde basic bevat, moet dan een agent noemen, in
agent of als agent:<naam> in model. Een beurt die er geen noemt, antwoordt 403 met agent
in error.param (op POST /messages begint de melding met agent:), en er wordt geen model
aangeroepen. De melding zegt de aanroeper een agent te noemen en nooit welk attribuut weigerde; het
auditspoor legt de reden direct turn forbidden vast en de id van de regel.
- Zonder de regel verandert er niets. Een beurt die geen agent noemt mag, tenzij een
forbidhaar raakt, dus de standaardregels bevatten geen regel overdirect_turn. Een regel erover kan alleen verbieden: eenpermitantwoordt422. Hij neemt geenmatchen geenactions, en inwhenalleennobody_namedenattribute_has, omdat een beurt geen labels draagt. - Regels kennen geen «niet». Verbied op een waarde die de personen die je inperkt altijd
hebben, zoals
plan: basic, nooit op «iedereen behalvepro», wat een regel niet kan zeggen. Een persoon wiens verzoek geenplanmeestuurt wordt niet geweigerd, en een verzoek dat niemand noemt ook niet. - Alleen beurten. Een rechtstreekse toolaanroep,
een kenniszoekopdracht buiten een beurt en een toolset via MCP blijven toegestaan. Wil je die ook
weigeren, voor elk verzoek van een sleutel, gebruik dan
settings.agents_required(Agents via de API). - Elk document kan weigeren. Een verbod in het document van je organisatie of in dat van de
sleutel telt allebei. Tooluitvoer insturen op een beurt die geen agent noemde, wordt beslist voor
de persoon die het insturen noemt. Een achtergrondrun wordt opnieuw
beslist als een worker hem start, voor de persoon die zijn verzoek noemde, dus een run die in de
rij stond voordat je de regel schreef, faalt met
agent_required. - Een vocabulaire controleert het attribuut. Met een vocabulaire
moet
planals attribuut zijn vastgelegd, en moetbasicin de gesloten verzameling liggen als het label er een heeft. - Een planning draagt alleen attributen als de persoon haar zelf schreef. Maak of wijzig je
een planning met de sleutel waaronder ze loopt en met
X-Core-Subjectvoor dezelfde persoon als haarsubject_id, dan bewaart Core de attributen die zij opgeeft versleuteld bij de planning, en elke beurt van de 30 dagen daarna past ze toe. Een regel overplanweigert haar geplande beurt dan zoals hij haar live beurt weigert. Elke latere wijziging door haar schuift de 30 dagen op, en een zonder attributen haalt ze weg;subject_attributes_untilop de planning zegt tot wanneer. Een planning die alleen je backend voor haar schreef, of die in de console is gemaakt, vuurt met alleen haarsubject_id, net als een planning waarvan de 30 dagen voorbij zijn. Een workflowstap draagt alleen desubject_idvan zijn run. Een planning wordt bij het schrijven gecontroleerd voor niemand in het bijzonder, dus een verbod met de voorwaardenobody_namedweigert het schrijven van een planning die eensubject_idnoemt, al zouden haar beurten, gemaakt voor die persoon, er doorheen gaan.
Registraties volgen de regels over tools¶
Toolsets, tool-aliassen, API-koppelingen, MCP-servers, SQL-koppelingen, scripts en zoektools zijn de
registraties waar tools uit voortkomen. Voor een verzoek dat een persoon noemt, beslissen de
regels over tool ook over die registraties: welke hun lijsten tonen (GET /toolsets,
/tool-aliases, /api_connectors, /mcp_servers, /sql_connectors, /scripts en
/knowledge/retrieval-tools), en of het lezen, wijzigen of verwijderen van een registratie mag.
Een registratie wordt beslist met haar eigen labels, de labels die haar tools dragen, en met de
actie die het verzoek vraagt: see om haar te tonen of te lezen, write om haar te wijzigen,
delete om haar te verwijderen. Een registratie die de persoon niet mag zien antwoordt 404; een
die ze ziet maar niet mag wijzigen of verwijderen antwoordt 403.
Elke registratie wordt beslist onder een toolnaam:
| Registratie | Beslist als |
|---|---|
toolset support |
support |
tool-alias lookup |
alias__lookup |
API-koppeling salaris |
api__salaris |
SQL-koppeling orders |
sql__orders |
script deck |
script__deck |
zoektool handbook |
knowledge__handbook |
MCP-server salaris |
zijn tools, mcp__salaris__<tool> |
Een MCP-server heeft veel tools, dus een regel beslist over de server aan de hand van de toolnamen die zijn patroon kan matchen:
- Een
permittoont de server als zijn patroon minstens één mogelijke tool ervan matcht:mcp__salaris__get_*,mcp__*en*doen dat;mcp__salarisniet. - Een
forbidverbergt de server alleen als zijn patroon elke mogelijke tool ervan matcht:mcp__salaris__*,mcp__*of*. Eenforbidvan een deel van de operaties, zoalsmcp__salaris__delete_*, houdt die tools weg bij het model en laat de server in de lijst staan.
Een verzoek dat niemand noemt wordt niet versmald: het toont elke registratie, wat de regels van de
sleutel over tool ook zeggen. De console toont ook elke registratie. Een registratie aanmaken, en
de versies van een koppeling of de bestanden van een script lezen, valt niet onder deze regels.
Wat een verzoek zonder subject bereikt¶
Een regel waarvan de when de persoon leest, geldt niet voor een verzoek dat niemand noemt: zo'n
permit staat niets toe en zo'n forbid weigert niets. Wat dat verzoek bereikt, beslissen de
regels zonder when en de regels met nobody_named. Onder de standaardregels is dat alles wat zijn
sleutel bereikt, van wie een rij ook is. Een achtergrondtaak die nooit een persoon noemt, blijft dus
werken nadat je de bibliotheek hebt ingedeeld, zolang de regels die je schrijft er een permit
voor houden, zoals de twee regels …-nobody-named hierboven. Laat je die weg, dan bereikt een
verzoek zonder persoon alleen wat de regels zonder when toestaan.
Zet settings.subjects_required op true en een verzoek zonder subject wordt geweigerd: 400 op
een modelroute, 422 op elke andere. Gebruik dit op een sleutel waarvan de aanroepen nooit mogen
vergeten voor wie ze zijn.
Als je organisatie een eigen policy heeft¶
Een organisatie kan een beleidsdocument van zichzelf hebben, naast dat van elke sleutel. Elke beslissing vraagt het dan aan allebei: iets wordt alleen bereikt als de regels van elk document het toestaan. Het document van je sleutel kan dus alleen versmallen wat de organisatie toestaat, nooit verbreden.
GET /key blijft antwoorden wat er op je eigen sleutel staat en zegt niets over het document van de
organisatie. Wil je dat lezen of schrijven, dan kan dat met GET/PUT/DELETE
/organisation/policy als je sleutel management heeft, en in de console op Instellingen van de
organisatie (Een organisatie). Heeft de organisatie geen
eigen document, dan antwoordt die route {"policy": null} en beslist alleen je sleutel.
DELETE /organisation/policy antwoordt 204, of er nu een document was of niet, dus je kunt hem
sturen zonder eerst te lezen. Een 404 op deze routes betekent dat je sleutel geen management
heeft. Een 403 betekent dat de organisatie boven de jouwe het sleutelbeheer aan zichzelf heeft
voorbehouden: lezen kan nog, wijzigen niet.
Loop je tegen een weigering aan die niet uit het beleid van je sleutel te verklaren is, kijk dan
daar: de twee documenten worden nooit tot één samengevoegd. Een forbid in het ene wordt nooit
opgeheven door een permit in het andere, en een soort waarvoor een document geen permit bevat,
wordt door dat document geweigerd, wat het andere ook zegt.
Wat een persoon noemen bewaart¶
Maakt een verzoek dat een persoon noemt een rij aan, dan hangt het van de soort rij af wat er met haar id gebeurt.
| Soort rij | Wat er wordt bewaard |
|---|---|
een gesprek, een opgeslagen beurt, een geheugenrecord, een datastore-record, een upload naar /files, een artefact dat een beurt maakte, en een kennisbron die je aanmaakt of uploadt (tekst, records, een gekopieerd bestand, een geüpload bestand) |
haar id, als eigenaar van de rij. De regels over de soort (thread, memory_record, stored_file, datastore_record of knowledge_source) beslissen wie de rij verder bereikt, en DELETE /subjects/{id} wist hem samen met de rest van haar gegevens |
| een definitie in de bibliotheek: een agent, een prompt, een skill, een API-koppeling, een MCP-server, een toolset, een tool-alias, een zoektool, een collectie, een index, een label | niets over haar. De definitie is van je organisatie. Een schrijfactie op een agent, een prompt of een workflow wordt voor haar beslist en haar auditregel noemt haar (hierboven), maar de definitie zelf houdt geen eigenaar |
| een planning | de persoon in het veld subject_id van de body, niet die in de header. Elke beurt die de planning afvuurt is voor die persoon. Wis je haar, dan wordt de planning uitgezet in plaats van verwijderd |
Een rij die is geschreven door een verzoek dat niemand noemde, heeft geen eigenaar. Die is gedeeld: onder de standaardregels bereikt elke persoon haar, en geen enkele wisactie haalt haar weg.
Een persoon wissen¶
DELETE /subjects/{id} verwijdert alles wat Core onder één subject-id bewaart, op de eigen
opslag van je organisatie:
- de gesprekken van die persoon met hun werkgeheugen, en de computers die er nog voor draaien;
- opgeslagen beurten, geheugenrecords, datastore-records, geüploade bronnen en artifacts, en de rechten erop;
- pagina's die voor die persoon zijn gepubliceerd, exports die tot die persoon zijn beperkt, en de feedgebeurtenissen over haar rijen;
- afgeronde taken die voor die persoon zijn gestart;
- haar workflowruns, waarbij een open run eerst wordt geannuleerd;
- verbetervoorstellen die alleen uit haar geheugen komen.
Sommige rijen blijven en verliezen alleen de persoon. Een beslissing die haar was toegewezen op een
workflowrun die blijft, houdt de run, zonder haar id of haar antwoord; een beslissing die nog op
haar wacht, mislukt met no_assignee, en de run gaat van daaruit verder. Een verbetervoorstel dat
uit meer mensen komt, houdt zijn tekst en verliest de id's van haar beurten. Een planning die beurten
voor die persoon afvuurt, en een gebeurtenistrigger die workflowruns voor haar start, wordt
uitgezet in plaats van verwijderd, zodat je hem op iemand anders kunt richten. Het antwoordt 202 -- het werk loopt op de achtergrond
-- en één auditgebeurtenis legt de aantallen vast zodra het klaar is. Audit- en verbruiksregels
houden de id zelf: dat spoor is jouw eigen verslag van wat er gebeurde, en de id alleen identificeert
niemand zonder de attributen die met het verzoek meekwamen.
curl -s -X DELETE "$CORE_URL/subjects/anna" \
-H "Authorization: Bearer $CORE_KEY"
GET /subjects/{id} geeft terug wat Core op dit moment voor die id bewaart -- een aantal per soort,
en verder niets, want meer heeft Core niet om mee te antwoorden. Beide routes staan achter de
subjects-capability.
De DELETE antwoordt ook 202 voor een id waarvoor Core niets bewaart, zodat je backend een
wisverzoek kan doorgeven zonder eerst uit te zoeken of de persoon bekend is. Wat de andere
antwoorden betekenen:
| Antwoord | Betekenis |
|---|---|
404 |
je sleutel heeft de capability subjects niet, of het id volgt de grammatica niet (letters, cijfers, . _ : @ -, 1 tot 128 tekens). Een 404 betekent nooit dat de persoon weg is |
401 |
de sleutel is niet geldig; dat geldt ook voor elke sleutel van een verwijderde organisatie |
403 |
je organisatie is opgeschort |
409 |
je organisatie wordt nog ingericht of wordt verwijderd. Probeer het later opnieuw |
Wil je zien wat er na het wissen over is, vraag dan GET /subjects/{id} op. Met de capability aan
en een geldig id betekent de 404 daarvan dat Core niets meer voor die persoon bewaart. Een beurt
die nog liep toen het wissen begon, wordt met rust gelaten en telt als skipped_in_flight in de
auditgebeurtenis van het wissen; stuur de DELETE opnieuw zodra die beurt klaar is om ook die weg
te halen.
Vraagt een andere sleutel van je organisatie om dezelfde persoon te wissen terwijl jouw wisopdracht
nog in de wachtrij staat of loopt, dan krijgt die sleutel hetzelfde erasure_id en begint er geen
tweede wisopdracht. In het spoor van die sleutel staat alleen zijn eigen gebeurtenis
subject.erasure_requested; de gebeurtenis subject.erased met de aantallen staat in het spoor van
jouw sleutel.
Anna, van begin tot eind¶
Alles samen: een virtuele medewerker voor accountantskantoren, één sleutel per kantoor, honderd
klantdossiers eenmalig geüpload met labels client en department, de MCP-server salaris gelabeld
department: payroll en websearch gelabeld audience: everyone. Anna, die op de loonadministratie
werkt bij kantoor Jansen, vraagt de assistent die haar kantoor heeft aangeschaft:
curl -s -X POST "$CORE_URL/responses" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-H 'X-Core-Subject: {"id": "anna", "attributes": {"department": ["payroll"], "office": ["jansen"]}}' \
-d '{"model": "<een model-id>",
"input": "wat is er gebeurd met de loonaangifte van De Vries vorige maand?"}'
Met de regels van hierboven van kracht, bereikt het zoeken alleen
bronnen gelabeld department: payroll of visibility: public -- het loondossier van De Vries, nooit
het belastingdossier van een andere klant, geüpload onder een andere afdeling -- en krijgt het model
de mcp__salaris__*-tools plus websearch aangeboden en verder niets. Het antwoord:
{
"id": "resp_9Ktq...",
"object": "response",
"status": "completed",
"output": [
{"type": "message", "role": "assistant",
"content": [{"type": "output_text", "text": "De loonaangifte van De Vries is ingediend op..."}]}
]
}
Bob van de receptie stuurt hetzelfde verzoek met {"id": "bob", "attributes": {"department":
["front-office"]}}: hij krijgt websearch aangeboden, geen enkele mcp__salaris__*-tool, en het
loondossier komt in zijn zoekresultaten niet voor.
Het eigen gesprek van Anna, als deze beurt op een gesprek staat, is van haar: een later verzoek met
een ander subject-id krijgt hetzelfde 404 als een gesprek dat nooit heeft bestaan. Van Anna zelf
wordt nooit meer bewaard dan "anna" en de attributen van dit ene verzoek.