Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Read permissions and restricted project groups

Every item belongs to a project group (or to none). A group is either

  • internal – the default: every signed-in user reads it, its members edit it; or
  • restricted – only its members (of any role, viewers included) and administrators see the group and anything in it: items, revisions, files and their versions, previews, BOMs, references, simulations, change orders and change requests about them. For everybody else it does not exist: they get exactly the “not found” a missing object gives.

The background and the alternatives that were considered are in the research note Read permissions for project groups.

Effective rights

Global roleMember of the groupInternal groupRestricted group
Adminany or noneread + editread + edit
CAD editoryesread + editread + edit
CAD editornoread– (not found)
Vieweryesreadread
Viewernoread– (not found)

Items without a group are readable by everyone and editable by every CAD editor (unchanged). A read-only member of a restricted group is a member with the global viewer role; there is no per-group read/write level.

Restricting a group

Project groups → the group → Details → Visibility: Restrict to members or Open to all users. Managers of the group and administrators may do this; the change is recorded in the audit log (project.visibility). A group can also start restricted (checkbox when it is created, or "visibility": "restricted" in POST /api/v1/projects). Add viewers as members to give read-only access. Linking a group to an identity-provider group (oidc_group) stays with administrators, because it grants read access.

Need to know: administrator access

By default an administrator reads every restricted group. A restricted group can limit that (Project groups → the group → Need to know, managers and administrators; PUT /api/v1/projects/{code}/policy {"admin_read": "implicit" | "with_reason" | "none"}):

  • implicit – today’s behaviour, the default (also when no policy was ever set);
  • with a reason – an administrator who is not a member sees the group as “not found” until they open an elevation with a written reason (10 to 1000 characters; POST /api/v1/projects/{code}/elevations, or the form on the group page). It lasts 60 minutes, can be ended early (DELETE …/elevations) and is audited (project.elevate, with the reason). The group’s managers get a notification, and the group page lists every elevation with administrator, reason and time;
  • none – administrators still manage the group (members, policy, the settings on the group page) but read nothing in it. An administrator cannot add themselves as a member or make themselves a manager: a manager of the group does that. There is no elevation under none.

Every change of the policy is audited (project.policy, with the previous value) and notifies the other managers. The policy applies to restricted groups only and needs at least one member who is a manager, so the group can be recovered; opening the group to all users makes the policy inert. While a policy is in force, an administrator who is no manager member can still add or remove other members, tighten the policy and rename the group, but cannot loosen the policy, open the group to all users, link another identity-provider group, add themselves or become a manager (403): those would undo the policy in one call. What an administrator can still do is limited by design: add another user (a manager sees it in the audit log), see that the group exists (code and name), and change operator-level things (database, backups, the command line) outside the application. A group linked to an identity-provider group before the policy was set keeps that link, so an administrator in that IdP group is a member. Webhooks cannot be scoped to, or redirected for, a hidden group; their deliveries are not shown. The elevation history keeps the username when an account is deleted; deleting the group deletes its history (the audit log keeps the records). The rule is part of the actor’s hidden groups (access::hidden_of_user), so every guard, the audit feed, change orders, notifications and the assistant honour it without a check of their own. This is step 1 of #151; the conditions on membership (step 2), the interface switches and the read log (steps 3 and 4) and the access review (step 5) follow below.

Conditions on membership (need to know, step 2)

Membership can be made not enough: Need to know: conditions on membership on the group page (managers and administrators; PUT /api/v1/projects/{code}/policy with the fields below) adds conditions every reader must meet as well. A reader who does not meet them is treated as a non-member – the group, its items, files and changes answer “not found” – and that holds for administrators too (they still manage the group). Nothing is set by default.

  • Required claims ("required_claims": [{"claim": "site", "values": ["main-office", "partner-workshop"]}, {"claim": "nda", "values": ["true"]}]; on the page one per line, site = main-office, partner-workshop): every condition must hold, each with one of its values, in the claims of the user’s last single sign-on (ID token and userinfo; compared without case; a list claim such as groups needs one of its entries). A change at the identity provider applies at the next sign-in. TeamCraft keeps only the claims that some group’s conditions name (users.idp_claims), so a user signs in again after a new claim condition is set. Local accounts have no claims and never meet a claim condition.
  • Allowed networks ("allowed_networks": ["10.20.0.0/16", "2001:db8::/32"]): the client address must be in one of the ranges – the same address as the sign-in throttle (the peer, or the entry the reverse proxy added to X-Forwarded-For; see Reverse proxy and TLS). A request whose address is unknown is outside.
  • Maximum sign-in age ("max_signin_age_hours": 12, 0 removes it): the group is visible only for so many hours after signing in – the start of the web session, and for tokens, the desktop app and notifications the user’s last sign-in. A person removed at the identity provider (or from the group’s IdP group) therefore loses the group at the latest then, even with a session that is still running (F7 in the threat model) – or at once, when the provider sends a back-channel logout.

Claims and the sign-in age are part of the SQL that builds a user’s hidden groups (access::hidden_of_user), so they apply to every channel, to change orders and to notification recipients; the network and the session’s own age are checked per request by the sign-in extractors and again at every step of an assistant run. A member refused by a condition is audited once per session or token (project.access_refused, with the reason and the address). The access review shows the conditions and, per member, whether they met them at their last sign-in; the evidence CSV carries both.

Setting conditions that would hide the group from yourself (your address outside the ranges, a claim your last sign-in did not have, a sign-in older than the age) is refused (409), so a manager does not lock the group away by mistake. Adding a condition, a narrower range, fewer accepted values or a shorter age is tightening, open to managers and administrators; anything else is loosening, for the managers while administrators are limited by the administrator policy. Every change is audited (project.conditions, old and new) and tells the other managers.

Interface switches and the read log (need to know, steps 3 and 4)

A restricted group can also close channels through which its data could leave the web UI, and can log who reads it (Project groups → the group → Need to know: interfaces and read log, managers and administrators; PUT /api/v1/projects/{code}/policy). Everything is allowed and nothing is logged by default; the switches build on a restricted group and are inert when the group is opened.

SwitchDenied: the group looks non-existent to …
api_tokensREST requests, MCP and the desktop app that sign in with a personal access token (the browser session still works)
mcpMCP tools (also through teamcraft mcp, which uses a token)
assistantthe built-in assistant, including the page context of a question
webhooksoutgoing webhooks: no webhook receives events of the group, scoped or not; an event that mentions the group in a change order is sent without the change
packagesPack and Go and BOM downloads (CSV, Excel): the root item’s group answers 403, items of such groups inside a structure are left out like restricted items of non-members
notification_contente-mail and chat notifications (not the in-app list): a notification about the group, or naming an item or change order of it, is sent with only a generic text (“new notification”) and the link, also as a direct chat message; the recipient reads the text after signing in
external_llmthe built-in assistant, unless the configured provider’s base URL is on the administrator’s list of internal / self-hosted URLs (TEAMCRAFT_ASSISTANT_INTERNAL_URLS, see Configuration); with assistant on and external_llm off the group is usable only with an internal model server
desktop_syncthe desktop app’s Get (working set GET /api/v1/items/{number}/revisions/{code}/workset): the root item’s group answers “not found”, items of such groups inside a structure are left out. The rest of the desktop app follows api_tokens

{"interfaces": {"mcp": false, "webhooks": false}, "read_log": true, "log_retention_days": 365} – every field is optional. Switching an interface off or the log on is open to managers and administrators; allowing an interface again, switching the log off or changing the retention is, while administrators are limited by the administrator policy above, for the managers (like loosening that policy). Every change is audited (project.interfaces, old and new values) and notifies the other managers. The in-app notification list and the notification e-mails of other groups are not affected by notification_content.

Read and download log. With read_log on, the group writes access_log entries (separate from the audit log): who (user name kept as text), when, the channel, the kind, the object (number or file name, kept as text), what read it (the tool, for tool reads) and the client address (with the same reverse-proxy rule as the sign-in throttle).

ChannelReads
webitem and revision pages, file downloads, Pack and Go and BOM exports in the browser
apithe same over the REST API and the desktop app (personal access token)
mcpread tools of an MCP client that name an item (get_item, get_revision, get_bom, read_file_text, …)
assistantthe same read tools called by the built-in assistant
desktopthe desktop app’s Get (the working set; its file downloads follow as api downloads)

Kinds: view (an item or revision page, the same data over the API, or a read tool), download (a file or file version), package (Pack and Go), export (a BOM CSV or Excel file), workset (a desktop Get). A tool read is one view of the item the tool was asked for, with the tool name as its detail (read_file_text: spec.txt names the file); searches, lists, change orders, write tools and failed tools log nothing, and the assistant’s own comparison calls (the taint check) are not logged. Repeated reads of the same object by the same user through the same channel within five minutes are one entry, so a resumed or ranged download is logged once. A read that cannot be logged does not happen (a tool answers with an error). Groups that do not log write nothing. A Pack and Go, BOM export or desktop Get writes one entry for every logging group inside the structure, not only for the root item’s group; a refused one writes nothing. The managers and administrators read the log, but only while they may read the group (an administrator shut out by admin_read gets “not found”; the labels are item content). The group page shows the newest 50 entries; The whole log, with filters (/projects/{id}/access-log) filters by user, kind, channel, object (part of the number, file name or tool), and a date range, 100 entries a page. The REST API pages the same way: GET /api/v1/projects/{code}/access-log?user=&kind=&channel=&object=&since=&until=&limit= answers {"entries": […], "next": id}; pass next as before= for the following page (null on the last). Times are RFC 3339 or YYYY-MM-DD (a date until includes that day). Not logged: thumbnails and 3D previews.

Retention and purge. log_retention_days (1 to 36500, empty = keep) is applied by teamcraft access-log purge (run it from cron or a timer): without options each group’s own retention, --project CODE limits it to one group, --older-than-days N deletes everything older than that instead. Every run is audited (access_log.purge, with the count). The database backup contains the log.

Access review

Managers of a restricted group and administrators open Access review on the group page (/projects/{id}/access-review). It lists every member with the account role, the role in the group, the source (added by hand or synchronised from the identity provider), whether the account is active, the last sign-in (flagged when there was none for 90 days) and the date the membership began, then the administrator policy (with the number of active administrators who are not members) and the administrator elevations with their reasons. I reviewed the access confirms the review with an optional note: the confirmation is dated, audited (project.access_review), tells the other managers and stores the member list as it was, so the question “who had access at the time” is answered later (GET /api/v1/projects/{code}/access-review/{id}). The evidence report is a CSV (one row per member, elevation and confirmed review, formula-safe) or the same data as JSON (GET /api/v1/projects/{code}/access-review, with ?format=csv for the CSV). The page is for restricted groups only; elevations are listed up to the newest 100 and reviews up to the newest 50 (the audit log keeps all).

Review schedule. Review the access every (days) (under Interfaces and read log, {"review_interval_days": 90}, 1 to 3650, 0 removes it) makes the review periodic: the group is review due when no review was confirmed within the interval (a group never reviewed is due at once). The flag shows on the group list (for the group’s managers and administrators), on the group page and on the review page, which also names the next due date; the report and the CSV carry the schedule. The hourly housekeeping reminds the group’s managers (an access_alert notification, which cannot be muted; the active administrators when the group has no manager) once when the review becomes due and again after each further interval without one; every reminder is audited (project.review_reminder). A shorter interval is tightening, a longer one or none is loosening (managers only while administrators are limited by the policy).

Moving an item out of a restricted group, or copying it with Save As into another group, needs a manager of the source group or an administrator – both declassify the item.

Review policy per project group

By default every reviewer of a change order must approve. A manager of a project group (or an administrator) can set a policy on the group page (Review policy) or with PUT /api/v1/projects/{code}/review-policy ({"min_approvals": 2, "required_reviewers": ["carl"], "role_group": "QA", "obsolete_requires_change": false}; GET shows it to anybody who sees the group):

  • Minimum approvals – this many approvals release the change; empty means all reviewers. A single rejection always rejects the change.
  • Required reviewers – people who must be reviewers of every change that touches the group and approve it.
  • Role group – another project group (e.g. QA); at least one approval must come from one of its members.

A change that touches several groups must meet the policy of each: the strictest minimum applies (a group without a value wants all reviewers), the required people and role groups add up. submit refuses a change that cannot meet them (409, naming what is missing: too few reviewers, a required person who is not a reviewer, no reviewer from the role group; a required person who may not review it, e.g. its author, gets a message of its own); the outcome of the review uses the same policy when a reviewer decides, so reviewers who have not decided yet stay pending once the change is released. The policy is read at decision time, not frozen at submit, but only the parts the assigned reviewers can still meet count (reviewers change in Draft only), so a policy edit after submit cannot strand a change in review. Deactivated people and people below the editor role do not count. Items without a project group count like a group without a policy: all reviewers must approve. A role group the reader cannot see is never named: the policy shows it as hidden and the submit message says “a required role group”. Changes are audited (project.review_policy, with the previous values). Step 2 of #152.

Obsolete and supersede only through a change order

Obsoleting a released revision is final and flags its dependents, and a supersession tells designers not to use an item any more. By default one CAD editor of the group can do both at once. The same form (Review policy on the group page) has a checkbox, obsolete_requires_change in the API (PUT /api/v1/projects/{code}/review-policy; omitted: unchanged, default off), that moves both through review:

  • With the setting on, Mark obsolete and Supersede on items of the group answer 409 for everybody, administrators included. Other groups are not affected.
  • A change order can carry an obsolete or supersede line next to its release lines (change page, On release; POST /api/v1/changes/{number}/items with {"item": …, "action": "obsolete"} or {"item": …, "action": "supersede", "successor": "PRT-000012"}; the revision defaults to the item’s latest). It needs a released revision that is in no other open change; a supersede line needs a visible successor item, not the item itself and no circle (400/409). The lines are allowed in every group, the setting only forbids the direct way.
  • The policy of the group (minimum approvals, required reviewers, role group) applies to the change; nothing happens before the last approval. Then, in the same transaction as the release, each revision becomes obsolete (impacts on dependents, revision.obsoleted webhook and notification as for a direct obsolete) and a supersede line also records superseded by. If a line cannot be applied any more (the revision was obsoleted in the meantime, the supersession would close a circle) the approval fails with 409 and the whole release stays undone. Rejecting or withdrawing the change changes nothing on the released revisions.
  • Supersede notifies the watchers of the superseded item, whether declared directly (with the setting off) or through a change order. Watchers who may not see the successor get the text without its number; the change page shows such a successor as restricted.

Step 3 of #152.

Requesters do not decide, concessions are confirmed

Two more checkboxes of the same form, both off by default (requester_cannot_decide and concessions_need_review in PUT /api/v1/projects/{code}/review-policy; omitted: unchanged):

  • Requesters do not decide their own change requests. The person who raised a change request on items of the group cannot accept, reject or defer it (403, administrators included); another CAD editor of the group does. The request page and GET /api/v1/change-requests/{number} show can.decide = false for them.
  • A reviewer confirms each “use as is” deviation. For an as-built revision of the group in a change order, every deviation with the disposition use as is is a concession that a reviewer of that change confirms on its own (Confirm in the deviation table, POST /api/v1/as-built-deviations/{id}/confirm) before any approval counts – approving earlier answers 409 and names the deviations. The person who recorded the deviation cannot confirm it (four-eyes). Changing a deviation clears its confirmation, and Revise copies deviations unconfirmed. The audit log records as_built.concession_confirm.

Step 9 of #152.

A visible item can point to a restricted one, and the other way round:

LinkWhat a non-member sees
BOM line, item reference, simulation basis to a restricted itema placeholder row “Restricted item”: find number, quantity, reference kind and basis role stay; number, name, revision, link and the notes (they describe the hidden item) are removed, and the line is never expanded. A hidden simulation basis never says whether it is out of date (that would tell when the hidden item is released or its file checked in). Placeholders are listed after the visible rows of their kind, so their position tells nothing about the hidden number. Editing such a line keeps its stored notes. When a non-member removes or edits it, the history names the real item for members (and is redacted for everybody else)
Mass and cost roll-upsleave restricted lines out; the BOM says “mass and cost exclude N restricted lines” (restricted_lines, rollup_incomplete in the API)
Where-used, referenced-by, simulations based on an item, impact analysisrestricted entries are left out and counted: “+N … in groups you cannot see”
CAD file-name matches (file references, suggestions, working sets)matches in restricted groups are left out without a count; the name shows as not in TeamCraft
Similar parts (shape search)restricted items are left out without a count, also when their file content is identical
Release readiness of a changea restricted unreleased dependency still blocks the release and shows as “restricted dependency not released – ask a member of its group or an admin”
Pack and Go, desktop Getrestricted items contribute no files; the manifest and the report only count them
Change propagation (impacts)a check on a visible item caused by a restricted release says “a restricted item this depends on was released”; the releasing change order is named only to readers who may see it (a change that also holds restricted items is left out). The same applies to the answer of “Acknowledge all”
Audit historyan entry about a link to a restricted item reads “… changed a link to a restricted item”; an entry whose text names restricted items or a change order the reader cannot see (the revisions of a submitted, rejected or released change, flagged dependents, a request’s items, a linked change) reads “… (the details name restricted items)” without details. Flagged dependents are listed per group, so the entries about visible dependents stay readable
Readiness of a changethe depending revision of a hidden item shows as “restricted item”; another open change the reader cannot see shows as “another open change order” (also in the error when an item is already part of one). Restricted dependencies follow the visible ones of their revision, and rows of hidden depending revisions come last – also in the error of a refused submit – so their place tells nothing about a hidden number
Change request items, change order itemsa restricted affected item is a placeholder row without its note, listed last
Save As out of a restricted groupthe copy’s history entry (“Saved … as a new item”) is redacted for readers who may not see the source; the copied file versions say “Copied from a restricted item”
Vault explorer folder of a restricted group“Folder not found”, exactly like an unknown folder id

Change orders and change requests

They have no group of their own. A change order is visible to administrators, its author, its reviewers and to everybody who can see every affected item (a draft without items: author, reviewers, admins). A change request is visible to administrators, the requester, the assignee and to everybody who can see every affected item (a request without items: everybody). Inside a visible change or request, rows of items the reader cannot see are placeholders.

Reviewers and assignees must be able to see every affected item; TeamCraft refuses others – also when an item is added later to a change with reviewers or to a request with an assignee. Submitting, reviewing and reopening a change needs the same – an author or reviewer who lost membership can still withdraw or cancel, but not approve blind. Adding a reviewer, assigning a request and the reviewer suggestions need it as well: whether somebody can see a hidden item would tell who belongs to its group, so an author who cannot see every item gets no suggestions and cannot add reviewers. A change cannot be submitted while one of its reviewers is deactivated.

Only the author of a change order and administrators edit, submit, withdraw, reopen or cancel it. When the author is deactivated, a manager of every project group the change touches may also withdraw, reopen or cancel it, or take it over (it becomes theirs; audited as change.handover). A change without items, or with an item outside any group, has no such manager.

Notifications, e-mail, chat, webhooks, assistant

  • Notifications are created only for recipients who may read their subject, evaluated per recipient. A text that also names other items or a change order (the revisions of a change, the change that released an item or implemented a request) is replaced by a shorter text without them for recipients who may not see all of them.
  • Notifications are checked again when they are read: after a group was restricted or a membership removed, the older notifications about it disappear from the list and the unread count, and pending e-mails and chat posts about it are not sent (marked skipped). They come back with the access. Pending e-mails and chat posts to a user deactivated since are not sent either. The groups of the items a notification names are stored with it (migration 0026), so it stays hidden from non-members after such an item is deleted.
  • With the deprecated webhook mode (TEAMCRAFT_MATTERMOST_DIRECT=false, posts in a shared channel), notifications about restricted groups show only “new TeamCraft notification” and the link – decided when the post is sent, so a group restricted after the notification was queued counts. E-mail and Mattermost direct messages of the bot go to one recipient and carry the full text.
  • A webhook sees its own group and the internal groups; a webhook for all groups receives no events of restricted groups. Scope a webhook to the group to get them (see Event webhooks). The revision.released event of a visible revision released by a change that also holds restricted items comes without the change object. A queued delivery whose group is restricted before it is sent is not sent (status failed, “Not sent: a project group of the event was restricted after it was queued”).
  • The MCP server and the built-in assistant use the user’s rights, so they see the same as the user. The assistant sends what a member reads to the configured model provider. It reads the user’s rights again before every tool call, so a membership removed during a run applies at once.
  • Text other users typed (request comments, names, notes) reaches the assistant and may try to make it copy restricted content into a comment or a draft that those users can read. Once a run has read something that a reader outside the user’s restricted groups would not get (that reader is also somebody else: what the user sees only as author, reviewer, requester, assignee or admin counts, and so does their worklist – also for a user who is in no restricted group), the page the question was asked on is such an item or change order, or a draft the assistant created or extended holds such content, the assistant’s text-writing tools (create_change_draft, create_change_request, comment_change_request) refuse for the rest of the run. External MCP clients act for the user without this check – the same caution applies as to any agent working with the user’s rights.

When changes take effect

Memberships and roles are loaded on every request (web, API tokens, desktop, MCP): removing a member takes effect on the next request. Nothing is deleted – watches and favorites of the lost items are hidden and come back with the access, check-outs stay (the removal message says how many). A manager of the group (or an admin) cancels them on the revision page or with POST /api/v1/items/{number}/revisions/{code}/cancel-checkout: managers may release the check-outs of users who are no longer members of the group or who are deactivated. An active member’s check-out a manager releases only with a reason (10 to 500 characters): Release with reason on the revision page, or {"reason": "…"} as the body of the request. The reason is stored in the audit log (revision.checkout_forced_release) and told to the holder in their notification; without one the release is refused (403). Other members cannot release it at all. Release N check-outs in the member list releases all of one person’s check-outs in the group at once (each audited on its own); administrators release everything a user holds from the user page. Season handover (/projects/{id}/season) does the same for many members at once, with one reason for all of them, and also sets member until, removes and demotes – each step with the rules of the single action. See When someone leaves and The end of a season.

Memberships synchronised from the identity provider change only at the user’s next SSO login; a user removed from the IdP group keeps access until then, and indefinitely with an API token. Revoke the user’s tokens or deactivate the user as well. Such a membership stays synchronised when its holder is made a manager (Demote lowers it again), and TeamCraft refuses to remove it while the user is active (it would come back at the next SSO login): remove the user from the IdP group, or have an administrator unlink the group or deactivate the user. Once the user is deactivated, a manager or administrator can remove it: SSO refuses deactivated users, so it cannot come back. A group needs at least one active manager; deactivated managers do not count when a manager leaves, is removed or is demoted (administrators may still do so). A manually added member who is also in the linked IdP group comes back (synchronised) at their next SSO login after being removed.

Files already copied to a desktop working folder stay on that computer; the desktop app marks such revisions as “No longer accessible” and never syncs them again.

Upgrading

Migrations 0024 to 0028 run on start and need no action: 0024 adds the visibility (every existing group stays internal, so nothing changes until a group is restricted), 0025 to 0028 add and back-fill what audit entries and notifications name. Take a backup first, as before every upgrade. Behaviour that changes for everybody, also without a restricted group:

  • Placeholder rows (“Restricted item”) exist only once a group is restricted; for internal groups BOMs, references and notes look as before.
  • An item of a restricted group that is listed on a change request cannot be deleted until it is removed from the request. Deleting an item of an internal group removes it from its requests and records that in their history.
  • A webhook for all groups stops receiving events of a group once it is restricted – scope a webhook to the group before restricting it if a receiver needs them.
  • Before restricting a group that shares the default numbering scheme, give it its own scheme (see below), or the shared counter keeps telling how many items it creates.

What stays visible

These are inherent to a shared instance and documented on purpose:

  • Item numbers, project group codes and change/request numbers are unique across the instance: choosing a taken one says so (“already in use”, the BOM import says “not available”).
  • The shared default number counter shows how many items other groups create. Give restricted groups their own scheme, e.g. {PROJECT}-{TYPE}-{SEQ:4}. A restricted group with a scheme of its own counts in a private counter: another group whose scheme expands to the same text (also one saved before the restriction, or for a later year or month) counts on its own, its numbering preview never shows the restricted group’s numbers, and no scheme is refused because of a restricted group. The private counter starts where the shared one stood when the group was restricted (numbers everybody could see then) and is merged back when the group is opened again. A generated number that is already taken by an item the user may see is skipped; one taken by a hidden item stops the creation (“already in use; change the scheme or enter a number”) instead of skipping on, so another group’s scheme tells of one existing number at a time, like typing a number in does – never how many there are, and it does not use up the restricted group’s numbers. (The built-in shared scheme keeps skipping: its counter is past every generated number, so it only meets typed-in numbers.)
  • A BOM line that would close a cycle is refused even if the cycle runs through restricted assemblies (“would create a circular BOM structure”): the structure must stay a tree for everybody.
  • Change and request numbers are one sequence for all groups.
  • Where-used and referenced-by counts show that restricted items use an item – its owner needs to know before making it obsolete or deleting it.
  • Audit entries written before this feature (migration 0024) are shown as stored; their numbers were visible to everyone at the time. Migrations 0025 and 0026 mark the older impact, change and request entries with the items and changes they name (also items listed in the details that have left the change or request since), so these are redacted like new ones. Older notifications get their subject (item, change, request) from their link (migration 0027) and the items and change orders their text names from the numbers in it (migration 0028, e.g. the released source of an impact notice), and are hidden like new ones; only those whose subject and named objects have been deleted since stay as they are.
  • Response times and caches: the preview meshes and shape signatures are stored per file content (SHA-256) for the whole instance. Somebody who uploads a file with exactly the same bytes as one in a restricted group can tell from how fast its preview and its similar-parts card are ready that the content exists already – they hold the file themselves, and no hidden item is named.
  • Audit entries and notifications remember the groups of the items they name when they are written (migration 0026). They stay redacted for non-members of those groups after the item is deleted or moved out of the group. An item of a restricted group that is an affected item of a change request cannot be deleted (remove it from the request first), so deleting it never makes a hidden request visible; a member who cannot see that request learns that one exists. Items of internal groups hide no request and are deleted with their request rows; each such request records the removal (with the item’s note) in its history and tells its people, without telling the deleter about requests they cannot see.
  • Response times of hidden and missing objects are not equalised.
  • Backups and the command line contain everything (operators). /admin/audit omits and redacts entries about groups hidden from the administrator by a need-to-know policy.