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 role | Member of the group | Internal group | Restricted group |
|---|---|---|---|
| Admin | any or none | read + edit | read + edit |
| CAD editor | yes | read + edit | read + edit |
| CAD editor | no | read | – (not found) |
| Viewer | yes | read | read |
| Viewer | no | read | – (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 asgroupsneeds 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 toX-Forwarded-For; see Reverse proxy and TLS). A request whose address is unknown is outside. - Maximum sign-in age (
"max_signin_age_hours": 12,0removes 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.
| Switch | Denied: the group looks non-existent to … |
|---|---|
api_tokens | REST requests, MCP and the desktop app that sign in with a personal access token (the browser session still works) |
mcp | MCP tools (also through teamcraft mcp, which uses a token) |
assistant | the built-in assistant, including the page context of a question |
webhooks | outgoing 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 |
packages | Pack 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_content | e-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 |
external_llm | the 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_sync | the 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).
| Channel | Reads |
|---|---|
web | item and revision pages, file downloads, Pack and Go and BOM exports in the browser |
api | the same over the REST API and the desktop app (personal access token) |
mcp | read tools of an MCP client that name an item (get_item, get_revision, get_bom, read_file_text, …) |
assistant | the same read tools called by the built-in assistant |
desktop | the 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}/itemswith{"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.obsoletedwebhook 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}showcan.decide = falsefor 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 recordsas_built.concession_confirm.
Step 9 of #152.
What outsiders see of links
A visible item can point to a restricted one, and the other way round:
| Link | What a non-member sees |
|---|---|
| BOM line, item reference, simulation basis to a restricted item | a 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-ups | leave 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 analysis | restricted 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 change | a 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 Get | restricted 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 history | an 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 change | the 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 items | a restricted affected item is a placeholder row without its note, listed last |
| Save As out of a restricted group | the 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.releasedevent of a visible revision released by a change that also holds restricted items comes without thechangeobject. A queued delivery whose group is restricted before it is sent is not sent (statusfailed, “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/auditomits and redacts entries about groups hidden from the administrator by a need-to-know policy.