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.
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.txtand 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: MyModelwithout boilerplate unless you need the explicit form.Query[T]andJson[T]support Pydantic models,msgspec.Struct, Pythondataclass, andTypedDicttypes — stdlib models are validated natively without pydantic (nested models,Annotated[int, Ge(13)]constraints), and a 422 lists every failing field as structuredissues. - 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, andpydantic.BaseModel. OpenAPI emitsoneOf+discriminator.mappingautomatically. - Three-scope DI —
SINGLETON,REQUEST, andTRANSIENTscopes 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_guardsand@use_interceptorswork on both HTTP@controllerand WebSocket@ws_controllerclasses natively. @propagate_metadata— Copy@use_guards,@use_interceptors,@use_middlewares,@use_encoder, and@set_metadataannotations from a source class or function to a target — thefunctools.wrapsequivalent 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_controllergateways with typed validated frames, discriminated-union dispatch (Discriminated[A | B, "key"]),BroadcastGrouprooms, and native guard/interceptor enforcement viaWsConnectionContext. - Server-Sent Events —
EventStreamwith keep-alive heartbeats andLast-Event-IDresumability for AI token-streaming patterns. - Typed streaming —
StreamingResponse[T]auto-negotiates between SSE, NDJSON, and JSON Lines based on the client'sAcceptheader. - 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 aUserInDB(or an ORM row) from a route annotated-> UserOutnever leakspassword_hash;"strict"also validates it.@raises(NotFoundError)documents error responses. - Pagination —
paging: OffsetPagination(orPageNumberPagination,CursorPagination[C]) and aPage[T]/CursorPage[T]return value give validated parameters with hard maxima, one envelope per app, RFC 8288Linkheaders, opaque HMAC-signed cursors with key rotation, and named OpenAPI components. - Custom responses & file delivery — Return your own
Responsesubclasses, stream downloads withawait Response.file(...), or emit XML withResponse.xml(...)while keeping the dispatch pipeline untouched. - Background tasks —
BackgroundTasksextractor fires work after the response is sent.TaskHandleexposesstatus,done,cancel()(before the task starts) andawait result()(from another coroutine). Signals notify on start, complete, and failure. - Application events — typed domain events between modules:
@on_eventhandlers on any provider, discovered and validated at startup; publish with the singletonEventBusor the request-scopedEventPublisher(inline handlers share the request's DI scope). Inline, concurrent or after-the-response delivery, priorities,propagate/isolateerror policies, shutdown draining, andrecording()/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_oncejobs on singleton methods with overlap control, jitter, timeouts, misfire handling, a DI scope per run, IANA time zones and aManualClockfor 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 withpython -m lauren execand tested withawait ctx.run_command(...). - Sessions — first-class signed-cookie sessions via
LaurenFactory.create(..., sessions=SessionConfig(...)). A mutablesession: Sessionis injected into handlers at zero per-request cost; the pluggable asyncSessionStoreships with an in-memory backend and a statelessSignedCookieSessionStore. HMAC-signed,HttpOnly/Secure/SameSite=Laxby default, withregenerate_id()andinvalidate(). Unsafe configuration is rejected at startup. Pair withcontent=ContentConfig(strict_content_type=True)so cross-sitetext/plainposts to JSON routes are rejected (415). - Stores & caching — one async key-value
Storeprotocol (boundedMemoryStorein core; Redis and friends plug in) behind sessions, session revocation, theCacheservice (get_or_setwith single-flight, tag invalidation, stale-while-revalidate),@cache_responseserver-side response caching with correct-by-default rules, and@idempotentIdempotency-Keyhandling. - Typed configuration — settings classes (frozen dataclasses, pydantic
models,
msgspec.Struct) loaded byConfigModule.for_root(...)from.env, TOML and the environment, validated insideLaurenFactory.create— oneConfigValidationErrorlists 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.AsyncTestClientand 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"), anddebug=Trueserves development error pages with the traceback and route chain. - OpenAPI 3.1 — Automatic schema generation from Pydantic models,
dataclasses,
TypedDict, andDiscriminatedunions. Swagger UI and ReDoc served out of the box. - Health & readiness probes —
HealthModule.for_root()serves/health/live,/health/ready,/health/startupand a full report from the publicapp.lifecycle_stateand@health_checkmethods on your singletons: per-check timeouts, concurrency, single-flight and caching, details hidden by default, andapp.begin_draining()to fail readiness before the socket closes. - Lifecycle signals —
SignalBuswithStartupBegin,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]"— addspydantic>=2.0for Pydantic-backed validation andPydanticEncoder.pip install "lauren[msgspec]"— addsmsgspec>=0.18for 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.
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:
CONTRIBUTING.md— setup, branch & commit conventions, and the quality bar.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)
| 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)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| 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 logRelease 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 logSecurity 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
Both files in this release have been uploaded with provenance metadata.
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 logA 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.