This site is being rebuilt and some pages are out of date. For current details, write to reach@inorbit.hr. This notice goes away when the rebuild is done.

No analytics unless you allow it, no tracking. This site keeps in your browser the language you pick, the theme, its colour, which site you chose, the currency on the pricing page and that you closed this notice; signing in adds session cookies. The legal page has the details.

Sign in

← Back to Platform

RFC 0064decided2026-10-06

Customers in the admin console, one page per person and per account

The console's admin People and Accounts pages become a customer area. A directory of people with how they sign in and what they hold, one page per person and one per account with everything the platform knows about them, and the few actions an admin needs (sign someone out, make them a partner, start or cancel an erasure, delete or restore a team). Admin-only at the edge and in the service, every action recorded with who, what and why, never a secret or what a customer wrote.

Problem

The console's admin side shows people and accounts as two flat lists. The People page answers four things per person: the subject, the address, the platform role and when we last saw them. Anything else means a database query or the identity provider's own admin interface:

  • whether their address is verified, whether a second factor is set up, how they sign in;
  • which accounts and teams they belong to, and what those use;
  • whether they asked for access, which agreements they were sent and which they signed;
  • whether an erasure is scheduled for them.

The service already read the whole identity for each row and kept only the role.

There was also no supported way to act on a person. Signing someone out, sending them the partner agreement without an access request, or starting an erasure for a person who asked by mail meant work outside the platform. Nothing recorded who did it or why.

Proposal

The directory

ListPeople answers more per person, still from one identity-provider read each:

  • their name and when the identity was made;
  • whether the address is verified and a second factor is set up;
  • the kinds of sign-in they use (password, oidc:google, passkey, ...);
  • how many accounts they belong to.

A new role filter narrows the list to one platform role. Roles live with the identity provider, so the filter reads the list in chunks and keeps the matches. A page reads at most 200 people and may come back short. The caller follows next_page_token until it is empty.

A person's page

GetPerson gathers, for one subject:

  • their accounts, with their role in each and when they joined;
  • their own account's units this month;
  • their access request, the agreements sent to their address and their signatures (version, the role it gives, state, dates);
  • active sign-in sessions and the latest consent per purpose;
  • whether an erasure is scheduled, and when it runs;
  • their role changes and the admin actions on them.

A signature shows its state and dates, never the typed name or the answers.

An account's page

GetAccountAdmin does the same for one account:

  • its owner and members, and its open invitations;
  • its keys and API tokens, by name and dates only;
  • this month's units, split by category, and how many domains it added;
  • its last 50 log entries and its unit changes;
  • the admin actions on it, and whether an erasure is scheduled.

A key's secret, its hash and even the last four characters of the secret stay out of the answer.

Actions

  • Sign out. The identity provider ends every sign-in session of the person, then the token issuer ends their consent and login sessions, so refresh and access tokens stop. If the first step fails, the second is not tried and the admin sees the failure.
  • Make a partner. The published agreement goes out as an invitation that gives partner once signed, the same path an approved access request takes (RFC 0057). An undecided request of theirs is approved with it. The admin chooses who signs: an individual (the person themselves, by their name) or a company (its registered name). A partner need not be a company (owner, 2026-10-07). The choice goes into the request as party_kind and is recorded as the kind of the agreement's party, so the invitation, the agreement's party block, the signature and the admin's lists all carry it. Without a kind the service infers it as before: a company when one is named or asked for, else the person. Change role lists partner too, and choosing it opens the same dialog: the role comes only from a signed agreement, never by hand. A person who is a partner already is refused.

A diagram is drawn here in the RFCs product; this page does not show diagrams yet.

  • Erasure of a person. The grace-period erasure a person can start for themselves (DeleteAccount), started by an admin with a required reason, and cancelled inside the grace window. Teams they own alone go with them; a team they own with other members blocks it, as it does for the person.
  • Erasure of a team. The team deletion with its grace window, without the admin being a member, and its restore. A personal account goes through the person's erasure instead.

A diagram is drawn here in the RFCs product; this page does not show diagrams yet.

An admin cannot sign out, erase or restore themselves from these pages; their own account page does that.

Audit

Every action writes one row of a new table, accounts.admin_actions: when, which admin, the action, the target (a person's subject or an account's id) and the admin's reason. The table refuses updates. Every action, and every read of a person's or account's page, also writes an audit line with who, what, the target and the outcome. Neither carries the reason, an address or anything the person wrote. The pages show the rows newest first.

Security and compliance

A diagram is drawn here in the RFCs product; this page does not show diagrams yet.

  • Admin only, twice. Every route is under /v1/accounts/admin/, which the edge already admits only for the platform's admin role, and the service checks the role again before it reads anything. Any other caller, including a team's own owner, gets PERMISSION_DENIED.
  • Least data. The pages show what an admin needs to support a customer: states, counts, dates and names. No secret, token, credential identifier, signature text or answer leaves the service. Sign-in kinds name the provider (oidc:google), never the account at the provider. A reason travels in a request body, never in a URL: cancelling an erasure is a POST, not a DELETE with a query string.
  • Accountability. Every action is a database row and an audit line, so a review can answer who signed a person out, who started an erasure and why. The rows outlive the person's erasure as the record that the action was taken; the target is a pseudonymous id that stops resolving.
  • Partner stays agreement-gated. Making a partner sends the agreement and nothing else. The role still comes only from a signature (RFC 0056, RFC 0057); an admin cannot set partner by hand.
  • Erasure keeps its grace period. An admin starts the same erasure a person can start for themselves, with the same grace window and the same sweeper. Nothing is erased at once, and a mistake can be undone inside the window.
  • Reads of personal data are logged. Opening a person's page is an audit line with the admin and the subject, so access to a customer's data has a trail.

Alternatives

  • The identity provider's own admin interface. It has the identities but none of the accounts, agreements, units or erasures, and no record of why something was done. It would also put a second admin surface on the internet.
  • A role column in our database for filtering. Faster filters, but a second copy of the role that can drift from the one the tokens carry. The chunked scan is slower and always right; at the platform's size it is enough.
  • Setting partner by hand. Simpler, and it would skip the agreement the role exists to require.

Open questions

  • Retention of accounts.admin_actions: the audit period is one year at least. A job that deletes older rows is not built yet.
  • Whether a person's data export should list the admin actions taken on them.

Status log

  • 2026-10-06: opened. Built in the accounts service: the richer ListPeople with the role filter, GetPerson, SignOutPerson, MakePartner, StartPersonErasure, CancelPersonErasure, GetAccountAdmin, DeleteTeamAdmin, RestoreTeamAdmin, the admin_actions table and its audit lines, with integration tests against a real database and mocked identity provider and token issuer. Next: the console's People pages, then its Accounts pages.
  • 2026-10-06: the console's People pages. The directory reads every person once and filters by role, sign-in and a search, with counts of who joined and who was active in the last week, who holds a role from an agreement and the open access requests. A person's page shows their profile, accounts and units, access request, agreements, what admins did to them, consent and erasure, and asks for a reason before every action.
  • 2026-10-06: the console's Accounts pages. The list reads every account once and filters by kind, plan, state and a search, with counts of accounts, teams, paid plans, probes and deletions. An account's page shows its people, open invitations, keys (names and dates, never a secret), this month's units by category and its log, and holds the plan move, grants, the probe mark and a team's deletion and restore. Subscriptions and invoices wait for the billing service's admin reads; the Billing tab says so.
  • 2026-10-06: live. The service side since accounts and protocol at 27004b08, the console's People and Accounts pages since console-ui at 738486ad. The Billing tab now shows the account's subscription (plan, seats, status, test or live mode, period end, the last failed payment) and its invoices with links to Stripe's own pages, read through the billing service's admin reads (#465); it ships with the next console roll.
  • 2026-10-07: Decided: built and live (#390 in accounts, #465 in console-ui).
  • 2026-10-07: a partner may be an individual (owner decision). MakePartnerRequest.party_kind (individual or company), the access-request path honours it, and NdaInvitation and PersonSignature carry the kind for the person's page and the NDA lists. The dialog asks who signs, the company's name only for a company, the country from a picker, the language, the reason, and says in one line what will happen. Change role lists partner and hands over to the same dialog. Audit lines carry the kind and the outcome, never a name or an address.

← Back to Platform