Skip to main content

identity

The cross-ecosystem identity contract for MiniAppPolis: principals and grants, not just authentication.

One specification with N conformant enforcement points — explicitly not a central auth service on the request path. A central service would put a network hop and a single point of failure in front of every request in the fleet. This repo instead defines what a correct enforcement point does, ships a binding that does it, and provides the fixture suite that proves any other binding agrees.

The design ships here, with the implementation. ecosystem-standards holds rules, not designs, and no service depends on it.

Installation

Install name: miniapppolis-identity (PyPI)  ·  Import namespace: identity

identity was unavailable on PyPI — an unrelated Microsoft auth library holds it — so the distribution is prefixed while the import namespace stays as every enforcement point already writes it.

[project]
dependencies = [
  "miniapppolis-identity>=2.0,<3",
]

A calling principal needs only the contract and the verifier. An enforcement point that resolves principals against a store adds the [store] extra, which pulls in SQLAlchemy:

dependencies = [
  "miniapppolis-identity[store]>=2.0,<3",
]
from identity import ChainVerifier, Principal, VerifiedSubject

Scope

In Out
Principals (human and machine), roles, scopes, explicit grants Session management, login UI, token minting
The four-function binding contract A runtime service other services call
The principal store schema A shared runtime store
The cross-language fixture suite Per-service business rules

Two ways to authenticate, one way to authorize

Machines and humans prove identity differently, and pretending otherwise produces a worse version of both.

Machines hold a named API key. The key is the name. Verification is a constant-time comparison against keys held in configuration — no token to mint, no issuer to call on the request path, and nothing for the caller to assert. Impersonating a machine requires possessing its key rather than claiming its name.

Humans hold issuer sessions. People need expiry, revocation and a login flow, which a long-lived string does not have.

Key material never reaches the principal store. Keys live in deployment configuration; the store holds names, status and roles. A database dump exposes no credentials, and rotating a key is a configuration change rather than a migration.

Both paths converge immediately after verification: one principal table, one role model, one authorization decision, one audit trail. authorize does not branch on how the caller was authenticated, and could not — by then the distinction is gone.

The four functions

Every enforcement point implements the same four, in the same order, once per request:

verify(credential)          -> VerifiedSubject     raises on failure
resolve(subject)            -> Principal | None    None is a valid answer
authorize(principal, scope) -> Decision            never raises
emit_audit(event)           -> None                never raises into the request

Four rather than one, because each step fails differently and each failure needs a different response. A collapsed is_allowed(token, scope) -> bool cannot tell "your token is forged" from "you are suspended" from "you are exactly who you say and simply lack this scope" — and cannot record any of them.

verify proves who the issuer says you are. resolve answers what this ecosystem knows about you. Keeping them separate is what stops sub being used as a primary key.

Principals

Humans and machines are the same shape and resolve through the same decision. kind exists for audit and policy, never as a proxy for privilege — privilege comes from roles, for both.

A principal's identity is (issuer, subject), and its identifier is a UUID this store owns. Two Clerk tenants can legitimately mint the same sub; multi-issuer is a design property, not an edge case. The two tenants stay separate: different products, different audiences.

Roles and scopes

Roles are named bundles of scopes. Roles are the only thing granted to a principal; scopes are the only thing checked at an enforcement point.

Scopes are exactly three dot-separated segments — <domain>.<resource>.<action>, e.g. wcs.notes.read. Three segments is a deliberate constraint: it keeps scopes greppable and makes wildcard expansion unnecessary.

Decision precedence is fixed and short:

  1. No principal → deny principal_not_found
  2. Suspended principal → deny principal_suspended
  3. Scope in any role → allow granted_by_role
  4. Explicit resource grant → allow granted_by_explicit_grant
  5. Otherwise → deny no_matching_scope

Suspension is checked before roles on purpose. Reversing 2 and 3 would make suspension advisory.

The principal store

One schema (sql/principal-store.sql), one instance per ecosystem database — not shared at runtime. The cogs + api-kaianolevine-com database gets one; deejaytools-com gets its own. They share the schema, never the rows.

It installs into its own identity Postgres schema so it can be added to an existing database without colliding with application tables.

Conformance

schema/ is the neutral source of truth. fixtures/ is what keeps every binding honest — the same inputs, the same expected decisions, in every language. authorize is the part that can be pinned exactly, because it is the only one of the four that is pure: no network, no database, no sink.

A binding is conformant when it passes the fixture suite. Nothing else counts.

Status

Early. The Python binding and the authorize fixture suite are real; verify and resolve reference implementations, the TypeScript binding, and the verify/resolve/audit fixture directories are not filled in yet.

Metadata

Release files for miniapppolis-identity 2.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for miniapppolis-identity 2.1.1
File Size Uploaded Provenance
miniapppolis_identity-2.1.1.tar.gz 9.8 MB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for miniapppolis-identity 2.1.1
File Interpreter ABI Platform
miniapppolis_identity-2.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 9.8 MB

Release files / miniapppolis_identity-2.1.1.tar.gz

Download URL miniapppolis_identity-2.1.1.tar.gz
Size 9.8 MB
Tags Source
SHA-256 checksum
How to use checksums
df9cec1ff62da0b3c2c5f89f76121df34697cf6d8970944271442d7de09d8fa6
BLAKE2b-256 checksum
How to use checksums
94391f89f90fa1e76d38a5714891cc3d53808610bfcf80494acd0742cf5100e0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / miniapppolis_identity-2.1.1-py3-none-any.whl

Download URL miniapppolis_identity-2.1.1-py3-none-any.whl
Size 26.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
17f686d7d751ea8c8b3e0b9bd14afc67af68f0e7b6bcabff43dac2722daa62f7
BLAKE2b-256 checksum
How to use checksums
952cccb76deb6be7bdd63c092bf0b7bf6eacf9334c4b5472c15fc0fba39a4250
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for miniapppolis-identity 2.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page