Skip to content

Concepts & glossary

The platform reuses a small vocabulary everywhere. Learn these eight or nine terms and the rest of the docs read quickly. Terms link to the place they're explained in full.

The tenancy model

Workspace

One client company — the universal linking entity. A workspace is minted by the Identity service and identified by a canonical workspaceId. Both products key their domain data on it: xifrasoft scopes invoices/companies/catalog to it, and figestió scopes its folder tree and files to it. A person who belongs to a workspace is a member. See Identity → Workspace.

Agency

An accountant firm. An agency groups the customer workspaces it serves and is identified by a canonical agencyId. Accountants are members of an agency; the agency holds grants over customer workspaces. Agencies are a figestió-integration concept — a standalone xifrasoft signup has no agency. See Identity → Agency.

Member

A person attached to a workspace or an agency, with a role.

  • Workspace member roles: owner · admin · member.
  • Agency member roles: accountant · admin.

Membership lives in the IdP graph and is projected into each product's local mirror via the token claims.

Grant (AgencyGrant)

The link { agencyId, workspaceId, scope } that lets an agency reach a customer workspace in both products. A grant carries a scope. Creating a grant is how a standalone xifrasoft customer is "attached" to an accountant without moving any data. See Identity → Agency.

Scope (read vs manage)

What a grant permits:

  • read — view the customer's documents/invoices, don't change them.
  • manage — act on the customer's behalf (e.g. assign a folder to the workspace).

The scope lives on the grant, not in the JWT. A resource server fetches the grant scope from the IdP (GET /agency/workspaces) and enforces it per endpoint. See Integration → read vs manage.

A workspace flagged billing-exempt because its agency pays off-platform (signed agreement + bank transfer, no Stripe object). Its subscription tier is set administratively per contract; it never sees checkout. Standalone xifrasoft signups are the opposite: they self-serve through Stripe. See Xifrasoft → billing.

Identity & auth

OIDC IdP (Identity Provider)

The single service that authenticates users and issues tokens. Everyone logs in here; nobody else issues tokens. Implements OpenID Connect authorization-code + PKCE. See Identity.

Resource server

A product API that consumes IdP tokens but never issues them. It verifies an incoming access token's signature against the IdP's JWKS, checks iss and expiry, reads the claims, and serves the named workspace/agency. Both the xifrasoft and figestió APIs are resource servers.

Access token / refresh token

A short-lived access token (default 15 min) authorizes API calls; a long-lived refresh token (default 30 days) buys a new access token without re-login. Both are RS256 JWTs bound to a stateful IdP session. See Identity → tokens.

Claims

The fields inside an access token: always sub (the user), role, accountStatus, plus either workspaceId + workspaceRole or agencyId + agencyRole — never both. A token is therefore either workspace-scoped or agency-scoped.

PKCE (S256)

Proof Key for Code Exchange. The client sends a hashed code_challenge up front and the matching code_verifier at token exchange, so an intercepted authorization code is useless. The IdP mandates S256. See Identity → login.

JWKS

The IdP's public keys, published at /.well-known/jwks.json. Resource servers fetch and cache them, then pick the right key by the token's kid header to verify the RS256 signature. Because verification is signature-based, no shared secret exists between services.

kid (key id)

The identifier in a JWT header that says which JWKS key signed it. The IdP defaults it to the RFC 7638 thumbprint of the public key, so key rotation is a matter of publishing a new key with a new kid.

Stateful session / immediate revocation

The IdP stores each session as a Mongo document. Deleting the document logs the user out everywhere on their next request — there is no "wait for the token to expire" window. This is a deliberate rejection of stateless JWTs.

Context switch ("select workspace" / "select agency")

Re-issuing a user's tokens with a different scope. Calling /auth/refresh?workspace_id= or ?agency_id= swaps the claims in place (no logout gap). Selecting a workspace is authorized by membership or an agency grant. See Identity → context switching.

Cross-cutting

id-unification

The platform keys a product's local row by the IdP's canonical id directly: User._id == sub, Workspace._id == workspaceId, Agency._id == agencyId. There's no bridge/mapping table. A product mints its local mirror lazily the first time it sees a verified token for an unknown id (see provisioning). The rows still never leave their own database.

Lazy provisioning

On the first verified token bearing an unfamiliar sub or workspaceId, a resource server fetches the profile from the IdP (/users/me, /workspaces/:id) and writes a local mirror row keyed by that id. Subsequent requests short-circuit. It keeps the product DBs self-sufficient without a sync job.

No-aud access tokens

IdP id tokens carry an aud (the OIDC client). Access tokens do not. That's intentional: one access token is valid at either product API, so an accountant session doesn't need separate tokens per product.

EMPRESES tree

The shared root folder that figestió syncs from the accountant's desktop via Syncthing. Its top-level subfolders are assigned to customer workspaces; everything beneath inherits that workspaceId. See Figestió → file-sync.

VeriFactu

Spain's AEAT e-invoicing regime. Xifrasoft submits each issued invoice over SOAP with an FNMT certificate and maintains a hash-chain of billing records. The legal deadline is 2027-01-01, on its own track. See Xifrasoft → VeriFactu.

TENANT_TYPE

A documented-but-unimplemented flag (saas | whitelabel) reserved for the day a partner needs a physically isolated deployment. Today everything runs one shared multi-tenant xifrasoft instance; isolation is by the agency boundary in code, not by deployment. See Integration → deployment.