Skip to main content

Core runtime library for the PaperDraft framework

Project description

paper-core

Runtime library for the PaperDraft framework. Provides auth, database, encryption, email, middleware, error handling, and audit logging — all importable under the paper.core namespace.

Status: pre-release · v0.1 in progress · Paper Plane Consulting LLC


Table of Contents

  1. Installation
  2. Module Reference

Installation

pip install paper-core

Requirements: Python 3.11+


Module Reference

paper.core.auth

JWT signing, password hashing, and FastAPI authentication dependencies.

from paper.core.auth import (
    Auth,                   # signs JWT access + refresh token pairs
    Password,               # Argon2 hash and verify
    Authenticate,           # FastAPI dep — validates JWT from header or cookie
    Authorize,              # FastAPI dep — validates JWT + enforces RBAC roles
    LoginAttemptLimit,      # FastAPI dep — rate limits login attempts per IP
    set_auth_params,        # call once at startup
    set_login_attempt_params,
    Claims, Key,            # token decode utilities
    Credentials, Signature, # request/response models
    Algorithm, TokenType, ClaimsKey, AuthErrorMessage,
)

Startup wiring (in dependencies.py):

from paper.core.auth import set_auth_params, set_login_attempt_params
from paper.core.auth.enums import Algorithm
from datetime import timedelta

set_auth_params({
    "public_key":     config.ENCRYPTION.PUBLIC_KEY,
    "excluded_paths": ["/auth/login", "/auth/refresh", "/health"],
    "alg":            Algorithm.RS256,
})

set_login_attempt_params(max_attempts=5, lockout_duration=timedelta(minutes=15))

Route usage:

from paper.core.auth import Authenticate, Authorize

@router.get("/me")
async def me(claims = Depends(Authenticate())):
    return claims

@router.delete("/{id}")
async def delete(claims = Depends(Authorize(["admin"]))):
    ...

paper.core.db

Async SQLAlchemy PostgreSQL repository with optional multi-tenant pool management.

from paper.core.db import (
    Postgres,                   # async SQLAlchemy repository
    ConnectionPoolConfig,       # pool settings
    Repository,                 # abstract base — extend to add engines
    FilterType,                 # EQUAL, LIKE, IN, NOT_IN, etc.
    MultiTenantPoolManager,     # one pool per tenant DSN
    MultiTenantDbDependency,    # FastAPI dep — resolves tenant DB from JWT
)

Single-tenant setup:

from paper.core.db import Postgres, ConnectionPoolConfig

db = Postgres(
    connection_string = config.POSTGRES_CONN_STRING,
    config            = ConnectionPoolConfig(
        future=True, size=10, max_overflow=5,
        recycle_after=3600, timeout=30, pre_ping=True,
    ),
)

Filtering:

results = await db.retrieve(
    UserEntity, UserModel,
    filter={FilterType.EQUAL: {"is_active": True}},
)

paper.core.security

RSA-OAEP encryption and random code generation.

from paper.core.security import Crypto, RSACrypto, Hasher, Pem, Encoding
# Static utility (key passed per call)
cipher = Crypto.encrypt_urlsafe(dsn, public_key_b64)
dsn    = Crypto.decrypt_urlsafe(cipher, private_key_b64)

# Instance-based (keys injected once)
crypto = RSACrypto(public_key_b64, private_key_b64)
cipher = crypto.encrypt_urlsafe(dsn)

# Random codes (invites, resets)
code = Hasher.generate(8)   # e.g. "aB3xZ9mK"

Encoding PEM keys for .env storage:

openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -pubout -out public.pem
base64 -w 0 private.pem   # → ENCRYPTION_PRIVATE_KEY
base64 -w 0 public.pem    # → ENCRYPTION_PUBLIC_KEY

Or with Pem:

from paper.core.security import Pem
print(Pem.to_base64("private.pem"))

paper.core.middleware

from paper.core.middleware import (
    HipaaResponseHeaders,       # X-Frame-Options, CSP, HSTS, etc.
    RequestIdMiddleware,        # X-Request-ID on every request
    RequestLoggingMiddleware,   # structured request/response logging
)

Registration order in main.py:

app.add_middleware(CORSMiddleware, ...)
app.add_middleware(HipaaResponseHeaders)
app.add_middleware(RequestLoggingMiddleware)
app.add_middleware(RequestIdMiddleware)    # executes first

Access the request ID downstream:

request.state.request_id

paper.core.errors

from paper.core.errors import ErrorHandler, ErrorMessage

ErrorHandler.handle(404, f"{ErrorMessage.NOT_FOUND.value} user {id}")
ErrorHandler.handle(401, "Unauthorized", {"WWW-Authenticate": "Bearer"})

All service layers raise through ErrorHandler — never construct HTTPException directly.


paper.core.audit

Generic audit logger. Accepts any DB, entity, and model — no framework-level schema dependency.

from paper.core.audit import Audit, AuditAction, AuditOutcome

audit = Audit(db=db, entity=AuditLogEntity, model=AuditLogModel)

await audit.log(
    event_type    = LoginEvent.SUCCESS,
    action        = AuditAction.ATTEMPT,
    outcome       = AuditOutcome.SUCCESS,
    email         = credentials.email,
    ip_address    = request.client.host,
    user_agent    = request.headers.get("user-agent"),
)

Audit failures are swallowed silently and logged — they never block the calling operation.


paper.core.email

SMTP email with framework lifecycle templates and themeable HTML.

from paper.core.email import (
    Server, Info, Body, Message,
    Subject, EmailTheme, EmailBodyParam,
)

server = Server(host, port, username, password)
sender = Info("My App", "noreply@myapp.com")

body = Body(
    subject = Subject.RESET_PASSWORD,
    data    = {
        EmailBodyParam.REDIRECT_URL.value: reset_url,
        EmailBodyParam.RESET_CODE.value:   code,
    },
)

msg = Message(
    sender    = sender,
    recipient = Info(user.name, user.email),
    subject   = Subject.RESET_PASSWORD.value,
    text      = body.text,
    html      = body.html,
)

server.send(msg)

Theming via environment variables:

EMAIL_THEME_PRIMARY_COLOR=#0057a8
EMAIL_THEME_BUTTON_COLOR=#0057a8
EMAIL_THEME_LOGO_URL=https://cdn.myapp.com/logo.png
EMAIL_THEME_COMPANY_NAME=My App

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

paper_core-0.1.13.tar.gz (35.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

paper_core-0.1.13-py3-none-any.whl (36.1 kB view details)

Uploaded Python 3

File details

Details for the file paper_core-0.1.13.tar.gz.

File metadata

  • Download URL: paper_core-0.1.13.tar.gz
  • Upload date:
  • Size: 35.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.6

File hashes

Hashes for paper_core-0.1.13.tar.gz
Algorithm Hash digest
SHA256 3b48dc05bd81c5e4347427a66fde1da675dba85662e127c27e82247ef802bb53
MD5 fd32d9895720865901e7a2a5e2d136b5
BLAKE2b-256 a6c488d63348b7c4e4d83132a8d9034f2a746e626e7c3e48d740507d93f2f699

See more details on using hashes here.

File details

Details for the file paper_core-0.1.13-py3-none-any.whl.

File metadata

  • Download URL: paper_core-0.1.13-py3-none-any.whl
  • Upload date:
  • Size: 36.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.6

File hashes

Hashes for paper_core-0.1.13-py3-none-any.whl
Algorithm Hash digest
SHA256 ecb91217f5e7a6ae175739d6a02e28f64b7f432994c0ffbb24be7bac01511913
MD5 6c1b0ada2f3a4d4ae2ebb1ccad47bbad
BLAKE2b-256 0f72252fba7bd5edbb62753cbd0503851b8cfed18d73a80866fda738eecc5ec1

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page