The management UI
A Vue 3 single-page application (tagshape-ui-management-web) — the only part of the product that is not C#. It
is a client of the account, library set and audit APIs and holds no state of its own beyond the session.
Signing in
The UI does not authenticate anyone itself. An unauthenticated visitor is sent to the
auth UI, which runs the provider round-trip and drops an x-tagshape-auth
cookie. A background service renews the token before it expires, which is why the token also travels as a cookie
rather than a header alone.
The UI reads the permission catalogue from GET /permissions and its own permissions from the JWT, and hides what
the caller cannot use. That is presentation, not enforcement — every one of these pages is calling an endpoint
that checks the same permission server-side.
Pages
| Route | Page | Needs |
|---|---|---|
/ | Home — panes summarising the library and recent activity | Varies by pane |
/librarysets | Library sets list | VariableList |
/librarysets/:id | One library set, its variables and their values | VariableRead |
/librarysets/new | Create a library set | VariableCreate |
/taggroups | Tag groups list | TagGroupList |
/taggroups/:id | One tag group and its tags | TagGroupRead |
/tags | All tags across groups | TagList |
/variablepreview | Resolve a set of tags and see which value wins | VariableRead |
/batch/import | Import a library file | BatchProcessingImport |
/batch/export | Export tag groups and library sets to a file | BatchProcessingExport |
/users | Users list | UserList |
/users/:id | One account, its groups and its API keys — group membership can be edited here too | UserRead |
/groups | Groups list | UserList |
/groups/:id | One group, its permissions and its members | UserRead |
/organisations | Organizations list | GlobalOrganizationList (global) |
/organisations/:id | One organization — providers, branding, patterns | GlobalOrganizationRead (global) |
/audit | The audit log, filtered and paged | AuditList |
/profile | Your own account and your API keys | JWT only |
Variables are reached through the library set that holds them rather than a page of their own — /variables
redirects to /librarysets.
Notable pages
Library set
Adding a variable, editing one's values, deleting one, and reading a sensitive value each check
library-set scope against this set's own ID, not just
whether the permission is held somewhere. A caller whose VariableUpdate is scoped to other library sets sees
the page (their VariableRead still covers it) but not the value editors on this one — the same as if they
held no VariableUpdate at all. VariableCreate is the one exception: creating a library set (as opposed
to a variable inside one) always needs it held unrestricted, so /librarysets/new and the page's own
create/save distinction do not narrow by ID.
Variable preview
Pick library sets and tags, and see what a consumer would be handed. It calls
GET /variablevalues/preview, so each variable comes back as the whole winning value — its tags, whether it is
sensitive, and its hint.
Each preview costs 10 credits, and is refused if the organization cannot afford it. (Opening a library set in the UI spends 1 credit too, since it is read with GET /variablesets/{id}; lists are
free.)
This is the fastest way to diagnose an unexpected value: the tags on the winner tell you whether the problem is a mistagged value or a tag you forgot to ask for. See Resolution.
Batch
The Batch section is shown to anyone holding BatchProcessingView, with an Import subsection and an Export
subsection each gated on its own permission - someone holding only one still sees the section, with just the
one subsection in it.
Import: a file picker, a summary of what the selected file holds (tag groups, tags, library sets, variables and how many are sensitive), and a replace toggle. It reports the same summary the API returns, including what a replacing import deleted. See Importing a library.
Export: two typeaheads for picking tag groups and library sets, and a choice of what a sensitive value
writes in the downloaded file - its actual value, its hint, its hash, or blank. The actual-value option is
offered only to someone holding VariableReadSecrets. See Exporting a library.
Group
Permissions are offered by category, read from GET /permissions. The page uses the same catalogue's dependency
graph to keep a group from being saved with a permission that would do nothing — rather than keeping its own copy
of the graph, which would eventually disagree with the server's.
Permissions the caller cannot grant are shown but not editable, which matches the server's behaviour of preserving them on save.
A ticked permission the catalogue marks librarySetScopable grows an All library sets / Specific library
sets choice beneath it. Choosing "Specific" reveals a chip-list-and-search picker — the same pattern the
tag picker on a variable's value uses — for naming exactly which library sets it should apply to; leaving it on
"All" (the default for anything not yet scoped) sends nothing extra, so an untouched permission behaves exactly
as it always has. See Library-set scoping.
Organization
Identity providers, logo and custom CSS. The Check credentials button calls
POST /organizations/identityproviders/check, which tries the credentials against the provider without saving
them — so a mistyped secret is found here rather than at somebody's login.
The custom CSS editor is a Monaco instance. Rules are filtered against an allow-list rather than injected wholesale.
The Client certificates section shows the primary and replacement slots, each with a badge for whether it
is TagShape-generated or client-provided, its valid-from/to dates, and its thumbprint — never a way to
download it. Generate and Upload (two text boxes for pasting a certificate and private key as PEM)
fill an empty slot; Remove clears one; Rotate certificates swaps the two once both are filled. All
four need GlobalOrganizationCertificateManage, shown but disabled without it — viewing the slots only needs
GlobalOrganizationRead, which the page already required to be here at all. See
Client certificates.
Profile
Your own account, your linked identities, and your API keys. Creating a key is the one place the key itself is
ever shown — copy it then, because only its hash is stored. The never-expiring option appears only if you hold
ProfileNeverExpiresApiKey.
A service account's page lists its keys in the same way, and shows the same New API key form to anyone
holding UserCreateServiceAccountApiKey — a service account has no login to make its own with. The form is not
shown on a person's page, because a person's key is theirs to make. Removing a key from either page needs
UserDelete.
Audit
Paged and filtered against POST /filter. Entries whose before and after state you may not read are shown with
the state withheld rather than hidden, so you can see that something happened even where you may not see what.
Variable types in the UI
A variable's type and whether it is sensitive are set once for the variable, next to its name, not per value. The type selector offers String, Multiline, Number, Boolean, JSON and Certificate; once a variable has been saved as Boolean the selector is locked, and Boolean is removed from the choices for every other variable's selector — neither direction can be switched into or out of after saving. The Sensitive checkbox is hidden entirely for a Boolean variable, and once ticked and saved it can no longer be unticked.
Each value's editor follows the variable's type: a plain text box for String, a number input for Number, a tick box for Boolean. Multiline, JSON and Certificate get a five-line box inline, with an Expand button that opens a larger popover editor for the same value — JSON's popover is a full Monaco instance (the same editor used for custom CSS on the organization page), so it gets syntax highlighting and folding; Multiline and Certificate get a bigger plain text area.
Sensitive values in the UI
Sensitive values render as a masked input with a reveal control. Where the caller lacks VariableReadSecrets
there is nothing to reveal — the API returned an empty value — and the hint is shown instead. See
Sensitive values.
Running it locally
cd src/tagshape-ui-management-web
yarn install
yarn serve
Under the Aspire host it is started as ui-management with its API references wired up. See
Architecture.