Access control (RBAC)
Not everyone in your organisation should see every document. With access groups you decide who sees what, for example that only HR sees HR files.
Access control is optional. You can create groups and label documents at any time, but Knovas only starts filtering once it is switched on for your account. Ask your Knovas contact to switch it on when you are ready (see Rolling it out).
How it works
- You create access groups, such as HR or Legal, arranged like an organisation chart.
- You label each document with the groups that may see it. A document without a label is visible to everyone in your organisation.
- When one of your users searches, your software tells Knovas which groups that user is in. Knovas returns only the documents that user may see.
Knovas does not know your individual users. Your software logs them in. Knovas trusts your software to send the right groups for each user. Access control keeps your users from seeing each other’s documents. It cannot protect you if someone steals your certificate: that person can act as any group. Keep your key safe.
Groups form a tree
Groups can sit inside other groups. A group can see its own documents and the documents of every group below it. It cannot see documents of groups above it or beside it.
All staff
├── Legal
│ └── Legal EU
└── HR| Document labelled | Who can see it |
|---|---|
| no label | Everyone in your organisation |
| Legal EU | Legal EU, Legal and All staff |
| Legal | Legal and All staff (not Legal EU, not HR) |
| HR | HR and All staff |
| Legal and HR | Anyone who may see either one: Legal, HR and All staff |
Step 1: Create your groups
Create top-level groups first, then groups inside them with parent. You can use a group’s name or its id wherever a group is expected. Names are not case-sensitive and must be unique.
# A top-level group
curl -X POST https://api.knovas.ch:8443/secured/access_groups \
--cert client_cert.pem --key client_key.pem --cacert ca_root_cert.pem \
-H "Content-Type: application/json" \
-d '{"name": "All staff"}'
# A group inside it
curl -X POST https://api.knovas.ch:8443/secured/access_groups \
--cert client_cert.pem --key client_key.pem --cacert ca_root_cert.pem \
-H "Content-Type: application/json" \
-d '{"name": "Legal", "parent": "All staff"}'import requests
BASE = "https://api.knovas.ch:8443"
AUTH = dict(
cert=("client_cert.pem", "client_key.pem"), # your certificate and private key
verify="ca_root_cert.pem", # Knovas' certificate
timeout=60,
)
groups = [
("All staff", None),
("Legal", "All staff"),
("Legal EU", "Legal"),
("HR", "All staff"),
]
for name, parent in groups:
r = requests.post(f"{BASE}/secured/access_groups", **AUTH, json={"name": name, "parent": parent})
r.raise_for_status()
print(name, r.json()["group_id"])To see all your groups as a tree, or rename and delete one:
# List all groups
curl --cert client_cert.pem --key client_key.pem --cacert ca_root_cert.pem https://api.knovas.ch:8443/secured/access_groups
# Rename a group
curl -X PATCH https://api.knovas.ch:8443/secured/access_groups/HR \
--cert client_cert.pem --key client_key.pem --cacert ca_root_cert.pem \
-H "Content-Type: application/json" \
-d '{"name": "People & Culture"}'
# Delete a group (only when it has no groups inside it and no documents)
curl -X DELETE https://api.knovas.ch:8443/secured/access_groups/Legal%20EU --cert client_cert.pem --key client_key.pem --cacert ca_root_cert.pemimport requests
BASE = "https://api.knovas.ch:8443"
AUTH = dict(
cert=("client_cert.pem", "client_key.pem"), # your certificate and private key
verify="ca_root_cert.pem", # Knovas' certificate
timeout=60,
)
# List all groups
tree = requests.get(f"{BASE}/secured/access_groups", **AUTH).json()["groups"]
# Rename a group
requests.patch(f"{BASE}/secured/access_groups/HR", **AUTH, json={"name": "People & Culture"})
# Delete a group (only when it has no groups inside it and no documents)
requests.delete(f"{BASE}/secured/access_groups/Legal EU", **AUTH)Step 2: Label your documents
When uploading: add access_groups when you start the upload. The document is protected from the first moment it can be found.
curl -X POST https://api.knovas.ch:8443/secured/init_document_transmission \
--cert client_cert.pem --key client_key.pem --cacert ca_root_cert.pem \
-H "Content-Type: application/json" \
-d '{
"identifier": "hr/2026/salary-bands.pdf",
"title": "Salary bands 2026",
"part_count": 2,
"access_groups": ["HR"]
}'import requests
BASE = "https://api.knovas.ch:8443"
AUTH = dict(
cert=("client_cert.pem", "client_key.pem"), # your certificate and private key
verify="ca_root_cert.pem", # Knovas' certificate
timeout=60,
)
r = requests.post(f"{BASE}/secured/init_document_transmission", **AUTH, json={
"identifier": "hr/2026/salary-bands.pdf",
"title": "Salary bands 2026",
"part_count": 2,
"access_groups": ["HR"], # use [] for "everyone"
})
r.raise_for_status()For documents you already uploaded: change the label with /secured/document_access. Send acting_as with your own groups: you may only give a document a group at or below your own (see the rules).
# Change who may see a document
curl -X PUT https://api.knovas.ch:8443/secured/document_access \
--cert client_cert.pem --key client_key.pem --cacert ca_root_cert.pem \
-H "Content-Type: application/json" \
-d '{"pointer": "hr/2026/salary-bands.pdf", "access_groups": ["HR"], "acting_as": ["All staff"]}'
# Check who may see a document
curl --cert client_cert.pem --key client_key.pem --cacert ca_root_cert.pem \
"https://api.knovas.ch:8443/secured/document_access?pointer=hr%2F2026%2Fsalary-bands.pdf"import requests
BASE = "https://api.knovas.ch:8443"
AUTH = dict(
cert=("client_cert.pem", "client_key.pem"), # your certificate and private key
verify="ca_root_cert.pem", # Knovas' certificate
timeout=60,
)
# Change who may see a document
r = requests.put(f"{BASE}/secured/document_access", **AUTH, json={
"pointer": "hr/2026/salary-bands.pdf",
"access_groups": ["HR"], # the new label ([] makes it visible to everyone)
"acting_as": ["All staff"], # the groups of the person making the change
})
r.raise_for_status()
# Check who may see a document
r = requests.get(f"{BASE}/secured/document_access", **AUTH,
params={"pointer": "hr/2026/salary-bands.pdf"})
print(r.json()["access_groups"])A document can carry up to 32 groups.
Step 3: Search as a user
Send the groups of the person who is searching with every search. Knovas returns only what they may see.
curl -X POST https://api.knovas.ch:8443/secured/query \
--cert client_cert.pem --key client_key.pem --cacert ca_root_cert.pem \
-H "Content-Type: application/json" \
-d '{"Input": "salary bands for engineers", "access_groups": ["HR"]}'import requests
BASE = "https://api.knovas.ch:8443"
AUTH = dict(
cert=("client_cert.pem", "client_key.pem"), # your certificate and private key
verify="ca_root_cert.pem", # Knovas' certificate
timeout=60,
)
def search_as(user_groups, question):
r = requests.post(f"{BASE}/secured/query", **AUTH, json={
"Input": question,
"access_groups": user_groups, # e.g. looked up from your own user database
})
r.raise_for_status()
return r.json()["results"]
search_as(["HR"], "salary bands for engineers") # finds the HR document
search_as(["Legal"], "salary bands for engineers") # does not| What you send | What the user sees |
|---|---|
"access_groups": ["HR"] | Unlabelled documents, plus documents for HR and any group inside HR |
"access_groups": ["HR", "Legal"] | Everything either group may see |
"access_groups": [] or nothing | Only unlabelled documents |
| A group name that does not exist | An error (404). The request is never run with fewer groups. |
Where access control applies
Once switched on, the same rule applies everywhere a document’s content could show up:
- Search and template filling: send
access_groupsin the request. - Deleting, rating and giving feedback on a document: send
access_groupsas well. - The knowledge graph: nodes, facts and reports only show what the user may see.
- Two lookups cannot carry groups: looking up a document by its id, and reading a document’s rating. Once access control is on, they only work for unlabelled documents.
In these requests, a document the user may not see behaves as if it did not exist: it is left out of results, or the answer is 404 rather than “forbidden”.
The requests that create groups and read or change labels (/secured/access_groups, /secured/document_access, /secured/documents_by_access) are not filtered by a user’s groups: they work on all documents in your account. Only call them from the administrator part of your software, never on behalf of an ordinary user.
Rules
- Higher groups see lower groups’ documents, never the other way round.
- No label means everyone. Label a document to restrict it.
- No groups sent means unlabelled documents only, not all documents.
- Mistakes stop the request. A group that does not exist is an error; it is never quietly ignored.
- You can only restrict within your own branch. Acting as Legal, you can label a document Legal or Legal EU, but not HR. Otherwise one team could hide documents from itself and push them into another team’s view. This check applies when you change a label afterwards; removing a label (making a document visible to everyone) is always allowed.
Check who can see what
List the documents labelled with a group, for example for an audit. Add include_descendants=true to include the groups inside it.
curl --cert client_cert.pem --key client_key.pem --cacert ca_root_cert.pem \
"https://api.knovas.ch:8443/secured/documents_by_access?group=Legal&include_descendants=true"import requests
BASE = "https://api.knovas.ch:8443"
AUTH = dict(
cert=("client_cert.pem", "client_key.pem"), # your certificate and private key
verify="ca_root_cert.pem", # Knovas' certificate
timeout=60,
)
r = requests.get(f"{BASE}/secured/documents_by_access", **AUTH,
params={"group": "Legal", "include_descendants": "true"})
answer = r.json()
print(answer["pointers"])
if answer["truncated"]:
print("There are more; raise the limit parameter (default 1000).")Rolling it out
- Create your groups (Step 1).
- Label your documents: new uploads with
access_groups, existing ones with/secured/document_access(Step 2). - Send each user’s groups with every search and document request (Step 3). While access control is still off, this changes nothing, so you can ship it safely in advance.
- Ask Knovas to switch access control on for your account, then test with a few users from different groups.
Limits and errors
| Limit | Value |
|---|---|
| Groups per account | 10,000 |
| Levels in the tree | 32 |
| Group name | Up to 128 characters, unique (not case-sensitive) |
| Groups per document | 32 |
| Groups per request | 32 |
| Code | Error | Meaning |
|---|---|---|
400 | invalid_group_name | The name is empty, too long or contains odd characters. |
403 | group_not_dominated | You tried to label something with a group outside your own branch. |
404 | unknown_group | A group you named does not exist. |
409 | duplicate_group_name | A group with this name already exists. |
409 | group_not_empty / group_in_use | You can’t delete a group that still has groups inside it or documents labelled with it. |
503 | enforcement_lookup_failed | Knovas could not check your settings. Nothing was shown; try again shortly. |
Questions? Write to contact@knovas.ch.
This page describes Knovas 1.3.0. Last updated .