Skip to content

tinyland-inc/tinyland-auth

Repository files navigation

@tummycrypt/tinyland-auth

Production-grade authentication system with TOTP, RBAC, and pluggable storage.

Consumption And Release Authority

The TypeScript import API stays under @tummycrypt/tinyland-auth. Tinyland's current release authority for this repo is Bazel-first:

  • CI validates the package through a repo-owned GloriousFlywheel runner lane and //:pkg //:test //:typecheck.
  • npmjs publication is disabled in package workflows.
  • GitHub Packages mirror publication uses @tinyland-inc/tinyland-auth, because GitHub Packages npm scopes are owner-bound.
  • Bazel consumers should depend through the Tinyland Bazel registry / BCR module path instead of relying on a workspace-local package copy.

pnpm add @tummycrypt/tinyland-auth is valid only when the consumer is configured for a registry that intentionally serves the @tummycrypt package scope. It is not the current Tinyland publication authority for this repo.

Exports

  • . — core auth: session management, password hashing, permissions, RBAC
  • ./sveltekit — SvelteKit integration: hooks, guards, CSRF, session cookies
  • ./storage — storage adapter interface + memory/file implementations
  • ./types — TypeScript type definitions
  • ./totp — TOTP generation and verification
  • ./activity — activity tracking
  • ./audit — audit logging
  • ./cred-gen — credential generation and display
  • ./validation — input validation utilities

Invitation Authority

@tummycrypt/tinyland-auth does not export an invitation service or factory. Use @tummycrypt/tinyland-invitation version >=0.2.5 for fail-closed invitation authorization, minting, acceptance, revocation, and lifecycle management, composed with the downstream application that owns user creation.

The type-only AdminInvitation, InvitationConfig, InvitationStorage, and invitation request/response DTO exports remain intentionally available, as do the invitation-record methods on the built-in storage adapters. These are compatibility surfaces for persisted data; they neither generate tokens nor authorize minting, acceptance, roles, or user creation.

The invitation 0.2.5 release has a per-token acceptance lock, but that lock is process-local. It serializes acceptance only within one Node.js process and is not a distributed or cross-replica compare-and-set. Consumers sharing invitation storage across processes or replicas still need a storage-backed CAS before claiming exactly-once acceptance.

Storage Adapters

Implement IStorageAdapter for your backend:

  • Built-in: MemoryStorageAdapter, FileStorageAdapter
  • Separate packages: @tummycrypt/tinyland-auth-pg (PostgreSQL), @tummycrypt/tinyland-auth-redis (Upstash Redis)

Tinyland Databaseless MVP

Tinyland's intended app shape is handle-first and email-less by default:

  • directory actors are keyed by handle
  • email is optional contact metadata, not identity authority
  • sessions bind capabilities to directory actors
  • FingerprintJS and Tempo are evidence/overlay planes, not auth credentials
  • GitHub OAuth is an app-local provider handoff that creates a normal package session after provider policy passes

"Databaseless" describes the storage model (no external database dependency), not a scale-out guarantee. The built-in FileStorageAdapter capability plane is single-replica dbless: safe for the single-replica deployments this package ships against today, but concurrent multi-replica writers need a storage-layer compare-and-swap that IStorageAdapter does not currently require or provide. Consumers running more than one replica need a CAS-capable adapter (e.g. a Postgres/Redis-backed IStorageAdapter) or must stay pinned to a single replica. The ha.tinyland.dev/* exception recorded on the mothership's staging manifests is the current, honest SSOT statement of that constraint in production — it documents the single-replica posture, it does not relax it.

See the Tinyland databaseless auth MVP and the executable example.

Browser fingerprint (anomaly-evidence signal, not an auth factor)

Fingerprint evidence (operator-canonized 2026-07-05, reworded 2026-07-11 to retire the "factor" framing): the Tempo-derived browser fingerprint (tinyland-fingerprint — UA-parse + Tempo evidence, NOT the fingerprintjs library) is an anomaly-evidence signal, not an enforced authentication factor at any layer. It is recorded, not vetoed, by design — a mismatch is logged and emitted as discarded OTLP evidence, and it never gates, destroys, or is required to establish a session (see the consuming app's fingerprintValidationHandle, e.g. tinyland.dev src/hooks.server.ts ~L926-944). Boundary invariant (TIN-1610, ratified): the print is evidence at the credential boundary only — validateSession() never destroys an authenticated session on a missing/changed fingerprint. Do not describe this mechanism as "browser-as-a-factor" in product docs; it supplies anomaly-detection evidence and a best-effort persistence hint, not a credential.

Cross-references: TIN-1610 (evidence-not-veto boundary invariant), @tummycrypt/tinyland-fingerprint v0.3.0 (supplier of the print + Tempo evidence), and prompts-enqueue golden-objectives §tinyland-auth (corrected 2026-07-05).

RBAC

Role management order and permission checks are intentionally separate. See the RBAC matrix for the package-owned matrix and downstream test guidance, and the role charter for the two-axis model and the P1/P2/P3 invariants.

Role x feature charter (operator-ratified 2026-07-04, TIN-2435)

Roles live on two axes: a governance spine (viewer -> member -> moderator -> admin -> super_admin, totally ordered by ROLE_HIERARCHY, governs who manages whom) and horizontal feature capability (ROLE_PERMISSIONS, an intentional lattice -- capabilities do NOT nest by rank; TIN-1606 precedent).

Role Axis Feature charter
super_admin governance-spine System owner; every permission.
admin governance-spine General administration across domains.
moderator governance-spine Fedi / community moderation.
editor specialist Blog editorial.
event_manager specialist Events / calendaring.
contributor specialist Drafts / submissions.
member governance-spine Self-service core (MEMBER_SELF_SERVICE_CORE).
viewer governance-spine Read-only admin surface.

Every role at or above member holds MEMBER_SELF_SERVICE_CORE (invariant P2), and every can* predicate derives from ROLE_PERMISSIONS -- there are no hand-maintained role arrays. Machine-readable charter: ROLE_CHARTER and PERMISSION_FEATURE_DOMAIN in src/types/permissions.ts.