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.4

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.4
File Size Uploaded Provenance
miniapppolis_identity-2.1.4.tar.gz 9.8 MB Without provenance Details

Built distribution (wheel)

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

Total release size: 9.8 MB

Release files / miniapppolis_identity-2.1.4.tar.gz

Download URL miniapppolis_identity-2.1.4.tar.gz
Size 9.8 MB
Tags Source
SHA-256 checksum
How to use checksums
8e7e775ceec315a3284aa458ba795fe7d1bd01eef120479c26d84d48aa410257
BLAKE2b-256 checksum
How to use checksums
fb310e7588c0c3689b4f8a6e239fe531101c489d7cc623bba2160a8f9f419456
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","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.4-py3-none-any.whl

Download URL miniapppolis_identity-2.1.4-py3-none-any.whl
Size 26.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ccc1e0affd1692cfd175109d9c42442f84669ba03da0add8ad0c9729357a5b97
BLAKE2b-256 checksum
How to use checksums
a33acf4c0643eead3ac991a843525e70dea71ba8805d61b5c1d65c750196688c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","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.4

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