Skip to main content

lauren

lauren framework: a metadata-first Python web framework. NestJS-style modules, Axum-style extractors, FastAPI-class ergonomics — resolved into an immutable execution graph at startup.

Test Lint CodeQL Coverage Package version Supported Python versions Downloads License Ruff Checked with mypy prek Discussions GitHub Stars


Documentation: https://lauren-py.dev

Docs MCP server (for AI coding agents): https://lauren-py.dev/api/mcp — setup guide

Source Code: https://github.com/lauren-framework/lauren-framework


For AI Agents & Coding Assistants

Install all skills in one command

# Claude Code, Cursor, Copilot, Continue, Codex CLI — auto-detected
npx skills add lauren-framework/lauren-framework

This copies all 60+ SKILL.md context packs into your agent's global skills directory (~/.claude/skills/, ~/.cursor/skills/, etc.). The next time your agent opens a Lauren project it has pre-loaded expertise on auth, database, API patterns, observability, security, and more.

Resource What it contains
lauren/llms.txt Framework overview — start here
lauren/llms-full.txt Complete reference — all APIs, patterns, common errors
AGENTS.md Agent rules, by-task lookup, file ownership, common errors, definition of done
CLAUDE.md Conventions, commands, golden rules, pattern selection guide
skills/ 60+ copy-paste skill guides covering every common task

Connect your agent to the live docs (MCP)

The docs site hosts a read-only MCP server so agents can search the current documentation instead of guessing from training data:

claude mcp add --transport http lauren-docs https://lauren-py.dev/api/mcp

Configurations for Cursor, VS Code, Windsurf, Gemini CLI and stdio-only clients: lauren-py.dev/mcp.

Full install guide and skills index: docs/guides/agent-skills.md


lauren is a modern, high-performance Python web framework for building APIs that need to fail at startup, not in production. It is built on these core ideas:

  • Metadata-first. Routes, dependency-injection bindings, module boundaries, lifecycle hooks, middleware, and guards are declared with decorators that attach metadata. They never rewrite your functions.
  • Startup-validated, runtime-pure. Every misuse — circular DI, missing module export, malformed extractor, conflicting routes — is rejected inside LaurenFactory.create(...), not on the first request.
  • No reflection on the request path. The whole graph is compiled into immutable structures at startup; serving a request is pure traversal.
  • AI-ready by default. Public surface is mirrored in llms.txt / llms-full.txt and exported in __all__ — a CI hook keeps the two in lock-step.

Features

  • Fast — Zero per-request reflection: routes, DI graph, extractors, and middleware are fully compiled at startup.
  • Implicit extractors — Path params, query strings, and JSON bodies are auto-detected from type annotations. Write id: int, q: str, body: MyModel without boilerplate unless you need the explicit form. Query[T] and Json[T] support Pydantic models, msgspec.Struct, Python dataclass, and TypedDict types — stdlib models are validated natively without pydantic (nested models, Annotated[int, Ge(13)] constraints), and a 422 lists every failing field as structured issues.
  • Pydantic-free discriminated unions — Discriminated[Cat | Dog, "kind"] routes tagged-union JSON bodies to the correct variant class using only stdlib. Works with @dataclass, TypedDict, msgspec.Struct, and pydantic.BaseModel. OpenAPI emits oneOf + discriminator.mapping automatically.
  • Three-scope DI — SINGLETON, REQUEST, and TRANSIENT scopes with Protocol bindings, multi-bindings (list[T]), and NestJS-style custom providers (use_value / use_class / use_factory / use_existing).
  • Pipes — Post-extraction value transforms: validate, coerce, or enrich parameters before they reach the handler. Function-based, class-based, chainable, and DI-aware.
  • Guards, Middleware & Interceptors — All three attachment points. Guards run first (allow/deny); middleware wraps raw request/response bytes; interceptors wrap handler execution for timing, caching, and response transforms. @use_guards and @use_interceptors work on both HTTP @controller and WebSocket @ws_controller classes natively.
  • @propagate_metadata — Copy @use_guards, @use_interceptors, @use_middlewares, @use_encoder, and @set_metadata annotations from a source class or function to a target — the functools.wraps equivalent for Lauren's decorator metadata.
  • lauren.reflect — Full metadata introspection API: static readers (reflect_controller, reflect_routes, reflect_guards, …), app-level readers (get_all_routes, get_all_ws_gateways, get_route_metadata), and rich result types (ReflectedRoute, ReflectedController, …). All readers honour the own-class rule — no metadata inheritance.
  • WebSockets — First-class @ws_controller gateways with typed validated frames, discriminated-union dispatch (Discriminated[A | B, "key"]), BroadcastGroup rooms, and native guard/interceptor enforcement via WsConnectionContext.
  • Server-Sent Events — EventStream with keep-alive heartbeats and Last-Event-ID resumability for AI token-streaming patterns.
  • Typed streaming — StreamingResponse[T] auto-negotiates between SSE, NDJSON, and JSON Lines based on the client's Accept header.
  • Response contracts — status_code= and typed results (Ok[T], Created[T], Accepted[T], NoContent) declare a route's statuses on the wire and in OpenAPI. response_validation="filter" projects the returned value onto the annotated type, so returning a UserInDB (or an ORM row) from a route annotated -> UserOut never leaks password_hash; "strict" also validates it. @raises(NotFoundError) documents error responses.
  • Pagination — paging: OffsetPagination (or PageNumberPagination, CursorPagination[C]) and a Page[T] / CursorPage[T] return value give validated parameters with hard maxima, one envelope per app, RFC 8288 Link headers, opaque HMAC-signed cursors with key rotation, and named OpenAPI components.
  • Custom responses & file delivery — Return your own Response subclasses, stream downloads with await Response.file(...), or emit XML with Response.xml(...) while keeping the dispatch pipeline untouched.
  • Background tasks — BackgroundTasks extractor fires work after the response is sent. TaskHandle exposes status, done, cancel() (before the task starts) and await result() (from another coroutine). Signals notify on start, complete, and failure.
  • Application events — typed domain events between modules: @on_event handlers on any provider, discovered and validated at startup; publish with the singleton EventBus or the request-scoped EventPublisher (inline handlers share the request's DI scope). Inline, concurrent or after-the-response delivery, priorities, propagate / isolate error policies, shutdown draining, and recording() / drain() for tests.
  • Hosted services & scheduling — @hosted_service() long-running loops that start after @post_construct, restart with backoff and stop before @pre_destruct; @interval / @cron / @run_once jobs on singleton methods with overlap control, jitter, timeouts, misfire handling, a DI scope per run, IANA time zones and a ManualClock for tests.
  • Commands & scripts — LaurenFactory.create_context(AppModule) runs the server's DI graph and lifecycle without HTTP; ctx.scope() gives scripts a request scope with deterministic teardown. @command("db:seed") turns a provider method into a CLI command — arguments derived from its signature, validated at startup — run with python -m lauren exec and tested with await ctx.run_command(...).
  • Sessions — first-class signed-cookie sessions via LaurenFactory.create(..., sessions=SessionConfig(...)). A mutable session: Session is injected into handlers at zero per-request cost; the pluggable async SessionStore ships with an in-memory backend and a stateless SignedCookieSessionStore. HMAC-signed, HttpOnly/Secure/ SameSite=Lax by default, with regenerate_id() and invalidate(). Unsafe configuration is rejected at startup. Pair with content=ContentConfig(strict_content_type=True) so cross-site text/plain posts to JSON routes are rejected (415).
  • Stores & caching — one async key-value Store protocol (bounded MemoryStore in core; Redis and friends plug in) behind sessions, session revocation, the Cache service (get_or_set with single-flight, tag invalidation, stale-while-revalidate), @cache_response server-side response caching with correct-by-default rules, and @idempotent Idempotency-Key handling.
  • Typed configuration — settings classes (frozen dataclasses, pydantic models, msgspec.Struct) loaded by ConfigModule.for_root(...) from .env, TOML and the environment, validated inside LaurenFactory.create — one ConfigValidationError lists every missing or invalid key with its source — and injected through DI. Secret[T] is redacted in logs, JSON and errors.
  • Testing toolkit — with TestClient(app) runs startup and the full shutdown on one persistent event loop, keeps a browser-like cookie jar, sends forms / uploads / streamed bodies, reads endless streams event by event and simulates client aborts. Test.create_testing_module(AppModule) .override_provider(X).use_value(fake).compile() swaps any provider, guard, middleware or module and is validated by the real factory. AsyncTestClient and an opt-in pytest plugin round it out.
  • Static files — StaticFilesModule.for_root("/assets", directory="./public") with ETag caching, Cache-Control, path-traversal protection, and dotfiles (.env, .git/) hidden by default (/.well-known/ still served).
  • Socket.IO — Engine.IO v4 / Socket.IO v5 adapter via @socketio_controller.
  • Stable error contract — A closed error catalog with stable codes and the everyday HTTP errors built in (NotFoundError, ConflictError, TooManyRequestsError, ServiceUnavailableError, …). Errors carry headers (UnauthorizedError(challenge=...), retry_after=), render as Lauren's {"error": {...}} envelope or RFC 9457 problem details (error_format="rfc9457"), and debug=True serves development error pages with the traceback and route chain.
  • OpenAPI 3.1 — Automatic schema generation from Pydantic models, dataclasses, TypedDict, and Discriminated unions. Swagger UI and ReDoc served out of the box.
  • Health & readiness probes — HealthModule.for_root() serves /health/live, /health/ready, /health/startup and a full report from the public app.lifecycle_state and @health_check methods on your singletons: per-check timeouts, concurrency, single-flight and caching, details hidden by default, and app.begin_draining() to fail readiness before the socket closes.
  • Lifecycle signals — SignalBus with StartupBegin, StartupComplete, ShutdownBegin, RequestReceived, RequestComplete, LifecycleStateChanged, and more.
  • Standards-based — Built on ASGI and anyio. Pydantic is optional (pip install "lauren[pydantic]").

Requirements

Python 3.11, 3.12, 3.13, and 3.14 are supported. Hard dependencies:

  • anyio — async backend and thread-pool offload for sync handlers.

Optional extras:

  • pip install "lauren[pydantic]" — adds pydantic>=2.0 for Pydantic-backed validation and PydanticEncoder.
  • pip install "lauren[msgspec]" — adds msgspec>=0.18 for struct-based serialisation.
  • pip install "lauren[full]" — installs both.

Installation

pip install lauren
# with an ASGI server:
pip install "uvicorn[standard]"
# or granian (faster on CPython):
pip install granian

Quick start

from pydantic import BaseModel
from lauren import LaurenFactory, controller, get, post, module, use_guards
from lauren.types import ExecutionContext


class CreateUser(BaseModel):
    name: str
    age: int


class AdminGuard:
    async def can_activate(self, ctx: ExecutionContext) -> bool:
        return ctx.request.headers.get("x-role") == "admin"


@controller("/users", tags=["users"])
class UserController:
    @get("/{id}")
    async def get_user(self, id: int) -> dict:
        return {"id": id, "found": True}

    @post("/", status_code=201)
    async def create(self, body: CreateUser) -> dict:
        return body.model_dump()

    @get("/admin")
    @use_guards(AdminGuard)
    async def admin_only(self) -> dict:
        return {"access": "granted"}


@module(controllers=[UserController])
class AppModule:
    pass


app = LaurenFactory.create(AppModule, docs_url="/docs")
uvicorn main:app --reload
# → http://127.0.0.1:8000/docs  (Swagger UI)

Dependency injection

from lauren import injectable, post_construct, pre_destruct, Scope

@injectable(scope=Scope.SINGLETON)
class UserRepository:
    @post_construct
    async def warm(self) -> None:
        self.cache: dict[int, str] = {}

    @pre_destruct
    async def flush(self) -> None:
        self.cache.clear()

    async def find(self, user_id: int) -> str | None:
        return self.cache.get(user_id)

    async def upsert(self, user_id: int, name: str) -> None:
        self.cache[user_id] = name


@controller("/users")
class UserController:
    def __init__(self, repo: UserRepository) -> None:
        self.repo = repo

    @get("/{id}")
    async def get_user(self, id: int) -> dict:
        name = await self.repo.find(id)
        if name is None:
            return {"id": id, "found": False}, 404
        return {"id": id, "name": name}


@module(providers=[UserRepository], controllers=[UserController])
class AppModule:
    pass

Modules

Modules are the unit of feature composition. Each module declares what it provides, what it exports, and what it imports from other modules — similar to NestJS:

@module(
    imports=[DatabaseModule, AuthModule],
    controllers=[UserController, ProfileController],
    providers=[UserService, EmailService],
    exports=[UserService],
)
class UsersModule:
    pass

Circular dependency detection, missing export errors, and scope violations are all caught at startup — before your first request.

SSE / streaming

from lauren import EventStream, ServerSentEvent, get

@get("/events")
async def stream(self) -> EventStream:
    async def generate():
        for i in range(10):
            yield ServerSentEvent(data=f"tick {i}", event="tick")
            await asyncio.sleep(1)
        yield ServerSentEvent(data="done", event="close")
    return EventStream(generate(), keep_alive=15.0)

For typed, content-negotiated streams (SSE / NDJSON / JSON Lines):

from pydantic import BaseModel
from lauren import StreamingResponse

class Tick(BaseModel):
    seq: int

@get("/ticks")
async def ticks(self) -> StreamingResponse[Tick]:
    async def gen():
        for i in range(100):
            yield Tick(seq=i)
            await asyncio.sleep(0.05)
    return StreamingResponse(gen())

Static files

from lauren.static_files import StaticFilesModule

@module(imports=[StaticFilesModule.for_root("/assets", directory="./public")])
class AppModule:
    pass

Or mount any ASGI sub-app:

app = LaurenFactory.create(AppModule)
app.mount("/static", StaticFiles(directory="static"))

Sessions

from lauren import LaurenFactory, SessionConfig, Session, controller, get, post, module

@controller("/account")
class AccountController:
    @get("/visits")
    async def visits(self, session: Session) -> dict:
        session["visits"] = session.get("visits", 0) + 1
        return {"visits": session["visits"]}

    @post("/login")
    async def login(self, session: Session) -> dict:
        session.regenerate_id()        # session-fixation defence
        session["user_id"] = "u-42"
        return {"ok": True}

    @post("/logout")
    async def logout(self, session: Session) -> dict:
        session.invalidate()
        return {"ok": True}

@module(controllers=[AccountController])
class AppModule:
    pass

app = LaurenFactory.create(
    AppModule,
    sessions=SessionConfig(secret="a-long-random-secret"),
)

The cookie is HMAC-signed and secure by default (HttpOnly, Secure, SameSite=Lax). Swap InMemorySessionStore for the stateless SignedCookieSessionStore or any shared key-value Store (adapted automatically) in production. Unsafe configuration fails inside LaurenFactory.create, not at runtime.

Caching

from lauren import LaurenFactory, controller, get, injectable, module, post
from lauren.cache import Cache, CacheModule, cache_response, idempotent

@injectable()
class Products:
    def __init__(self, cache: Cache) -> None:
        self._cache = cache

    async def get(self, pid: int) -> dict:
        return await self._cache.get_or_set(
            f"product:{pid}", lambda: load_product(pid), ttl=60, tags=(f"product:{pid}",)
        )

@controller("/products")
class ProductController:
    def __init__(self, products: Products) -> None:
        self._products = products

    @get("/{pid}")
    @cache_response(ttl=30, vary=("accept-language",))   # server-side; guards still run first
    async def show(self, pid: int) -> dict:
        return await self._products.get(pid)

    @post("/{pid}/orders")
    @idempotent(ttl=24 * 3600)                           # Idempotency-Key retries run once
    async def order(self, pid: int) -> dict:
        return {"ordered": pid}

@module(imports=[CacheModule.for_root()], providers=[Products], controllers=[ProductController])
class AppModule:
    pass

Concurrent misses share one computation, cache.invalidate_tags(...) evicts in O(1) without scanning keys, and a cached response never replays a Set-Cookie. Every stateful feature runs on one Store: StoresModule.for_root(default=...) plus SessionConfig(store=...) share a single backend.

Configuration

from dataclasses import dataclass

from lauren import LaurenFactory, injectable, module
from lauren.config import ConfigModule, Secret

@dataclass(frozen=True)
class DatabaseSettings:
    url: str
    pool_size: int = 10

@dataclass(frozen=True)
class AppSettings:
    session_secret: Secret[str]       # redacted in repr, logs and JSON
    database: DatabaseSettings        # APP_DATABASE__URL, APP_DATABASE__POOL_SIZE
    debug: bool = False               # APP_DEBUG=yes

@injectable()
class Mailer:
    def __init__(self, settings: AppSettings) -> None:
        self.debug = settings.debug

@module(imports=[ConfigModule.for_root(AppSettings, env_prefix="APP_")], providers=[Mailer])
class AppModule:
    pass

app = LaurenFactory.create(AppModule)   # raises one ConfigValidationError listing every bad key

Values come from .env, .env.{profile} and the environment (the environment always wins; os.environ is never written). Profiles, TOML / YAML / secret-file sources, feature-scoped settings and load_settings(...) for factory kwargs: see the Typed Configuration guide.

Testing

from lauren.testing import Test, TestClient

app = Test.create_testing_module(AppModule).override_provider(Mailer).use_value(FakeMailer()).compile()

with TestClient(app) as client:                 # startup() on enter, shutdown() on exit
    client.post("/account/login")               # Set-Cookie kept in client.cookies
    assert client.get("/account/visits").json() == {"visits": 1}
    with client.stream("GET", "/events") as r:  # endless SSE, event by event
        first = next(r.iter_sse())

One event loop per client keeps startup-created resources (pools, asyncio primitives, background tasks) alive across requests. See the testing guide.

Hosted services & scheduling

from lauren import injectable, module
from lauren.scheduling import ScheduleModule, ServiceContext, cron, hosted_service, interval

@hosted_service(restart="on-failure")
class OutboxRelay:
    async def run(self, ctx: ServiceContext) -> None:
        while await ctx.sleep(1.0):          # False once shutdown asks it to stop
            ...                              # relay one batch

@injectable()
class Maintenance:
    @interval(seconds=60, jitter=5)
    async def warm_cache(self) -> None: ...

    @cron("0 3 * * *", tz="Europe/Paris", timeout=300)
    async def purge_sessions(self) -> None: ...

@module(imports=[ScheduleModule.for_root()], providers=[OutboxRelay, Maintenance])
class AppModule:
    pass

Services start after @post_construct and stop, in reverse order, before @pre_destruct; jobs fire once the app is ready. Bad cron expressions, missing ScheduleModule, non-singleton providers and duplicate names fail at startup. See the Scheduling guide.

Commands

Admin and maintenance tasks are commands, not HTTP endpoints:

from typing import Annotated

from lauren import injectable
from lauren.commands import CommandContext, Opt, command

@injectable()
class SeedCommands:
    def __init__(self, db: Database) -> None:
        self._db = db

    @command("db:seed", help="Insert fixture data.")
    async def seed(
        self,
        ctx: CommandContext,
        *,
        count: Annotated[int, Opt(short="-n", help="rows per table")] = 50,
        reset: bool = False,
    ) -> int:
        if reset and not ctx.confirm("Truncate every table?"):
            return 1
        ctx.echo(f"seeded {await self._db.seed(count)} rows")
        return 0
$ python -m lauren exec app.main:AppModule db:seed -n 500 --reset --yes
seeded 500 rows

The command runs with the app started (@post_construct before, @pre_destruct after) in its own DI scope; the exit code comes from the return value. python -m lauren commands lists commands, python -m lauren console opens an async REPL on the app, and LaurenFactory.create_context(AppModule) does the same for your own scripts. See the Commands & Scripts guide.

Performance

Runtime is pure traversal of pre-compiled structures — no inspect.signature(...), no get_type_hints(...), no isinstance(...) walking on the hot path. The DI graph, route table, extractor bindings, and middleware pipeline are all resolved once at startup. Each request pays only the cost of dispatching through the already-compiled graph.

Benchmarks

Cross-framework numbers are produced by a separate harness: benches/bench.py drives every participant through the same workloads, one fresh process per measurement, and writes benches/results/frameworks-inprocess.json. The figures below are drawn straight from that committed artifact — median throughput per workload, then the p50/p95/p99 round trip per participant — with error bars spanning the min–max range across repeats, so the spread sits next to the median instead of behind it. The latency error bars are capped at 2.7 ms so one 10.5 ms range cannot flatten the other workloads: that single range stops at the cap and its true maximum is written above a break marker. The client calls each application through httpx.ASGITransport: no socket, no server process, no TLS. Read them as the cost of the framework layer itself, not as a deployed-configuration number. The full tables, the method and the pinned dependency set are on the benchmark page.

Median throughput by workload, one bar per participant, with error bars spanning the min–max range across the repeats p50, p95 and p99 round trip per participant, one panel per workload, with error bars spanning the min–max range across the repeats; colour marks the participant, and the one range past the 2.7 ms cap is cut off with its true maximum written above the break

Optional dependencies

Package Purpose
uvicorn / hypercorn / granian ASGI server (none bundled — pick one)
pydantic / msgspec Model validation and OpenAPI for those model kinds (lauren[pydantic], lauren[msgspec], lauren[full])
orjson Faster JSON via OrjsonEncoder — opt in with json_encoder=OrjsonEncoder() or json_encoder=auto_encoder()
msgspec Alternative fast JSON encoder via MsgspecEncoder
pyyaml YamlSource for typed configuration (lauren[yaml]); TOML, .env and the environment need nothing extra
httpx Only if you prefer httpx.ASGITransport in tests — lauren.testing.TestClient needs nothing extra

Form bodies and multipart uploads are parsed in-tree; no python-multipart needed.

Companion packages

Package Purpose
lauren-middlewares CORS, rate-limit, GZip, security headers, request-id, trusted hosts, HTTPS redirect, body-size limit, timeout
lauren-logging Structured logging module with processor pipeline, contextvars binding, pluggable backends (stdlib, structlog, file, fan-out)
lauren-guards Auth guards: JWT bearer, API key, basic auth, OAuth2 introspection, session cookie, RBAC/ABAC, CSRF, IP allowlist

Deployment

lauren is a standard ASGI application — deploy exactly like FastAPI or Starlette:

# Uvicorn
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4

# Granian (Rust-based, faster on CPython)
granian --interface asgi main:app --host 0.0.0.0 --port 8000

# Hypercorn (HTTP/2 + HTTP/3)
hypercorn main:app --bind 0.0.0.0:8000

Import HealthModule.for_root() and point the orchestrator's probes at it — liveness never checks dependencies, readiness fails before startup completes and while draining:

startupProbe:   {httpGet: {path: /health/startup, port: 8000}, periodSeconds: 2, failureThreshold: 30}
livenessProbe:  {httpGet: {path: /health/live, port: 8000}, periodSeconds: 10}
readinessProbe: {httpGet: {path: /health/ready, port: 8000}, periodSeconds: 5}
lifecycle:
  preStop: {exec: {command: ["sleep", "5"]}}

See the health checks guide for draining and @health_check.

Contributing

We welcome contributions of every size, from typo fixes to whole subsystems. Read first:

  1. CONTRIBUTING.md — setup, branch & commit conventions, and the quality bar.
  2. AGENTS.md — the design invariants every PR must respect, whether the author is human or an AI agent.
uv tool install prek      # one-time
prek install              # wires up the git hook
nox                       # lint + tests + typecheck

License

MIT — see LICENSE.

Metadata

Release files for lauren 1.11.0

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

Source distribution (sdist)

Source distribution for lauren 1.11.0
File Size Uploaded Provenance Actions
lauren-1.11.0.tar.gz 3.4 MB With provenance Details for lauren-1.11.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for lauren 1.11.0
File Interpreter ABI Platform Actions
lauren-1.11.0-py3-none-any.whl Python 3 none any Details for lauren-1.11.0-py3-none-any.whl

Total release size: 4.2 MB

Release files / lauren-1.11.0.tar.gz

Download URL lauren-1.11.0.tar.gz
Size 3.4 MB
Tags Source
SHA-256 checksum
How to use checksums
41c22fe71e4b36f856fd65cd25404d5855e7c9e9ee8623c16328cb96e7d427b6
BLAKE2b-256 checksum
How to use checksums
c98ce1d42863fb8a98d3c5d774b63c807d5a1db3eda4cc98aabd4b53575cd1bc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 5, 2026.

Transparency log

Release files / lauren-1.11.0-py3-none-any.whl

Download URL lauren-1.11.0-py3-none-any.whl
Size 755.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9b8da73bd00d2750fd3fb97791d83b69afab76eef75c22d12c2c8d4088a1b047
BLAKE2b-256 checksum
How to use checksums
db2ad5a0c8cb8241330147c0c7538fc187b08b82acb63fd1e5ace0bfa159461a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 5, 2026.

Transparency log

Security for lauren 1.11.0

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for lauren-1.11.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 5, 2026.

Transparency log

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.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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