Skip to main content

faceplant

Build a faceplant platform service in ~30 lines. Identity, the 30-second broker heartbeat, dashboards, the runtime plugin host, the daily garbage collector, and MCP tool serving are all under the hood — you write domain code.

pip install faceplant

Quickstart — a whole service

from faceplant import Service, Table, Column

svc = Service(
    "billing",
    description="Track usage bills.",
    icon="receipt",
    category="tools",
)

@svc.get("/data")
async def bills(user):                       # user.email, user.slug, user.dir
    rows = load_bills(user.dir)              # per-user private storage, created for you
    return Table(
        title="Bills",
        columns=[Column(key="month", header="Month"),
                 Column(key="amount", header="Amount")],
        rows=rows,
    )

@svc.responder("billing.total")              # a broker API other services can call
async def total(data):
    return {"total": sum_bills()}

app = svc.app                                # uvicorn src.main:app

That's the entire service. Service(...) already gave you:

  • a gateway card heartbeated every 30s (stop the pod → the card disappears);
  • /health, /manifest, /_plugins and CORS;
  • identity: user is injected from the platform JWT (401 when absent), with user.dir for per-user storage and user.tags / user.services;
  • the runtime plugin host: other services can extend this one with signed wheels, with zero code here (svc.registry if you want to add hook points);
  • the daily GC that reclaims a user's data ~10 days after they're gone.

More surfaces (each one decorator)

@svc.consumer("CHAT", filter_keys=["chat.>"])   # subscribe to broker events
async def on_chat(data): ...

@svc.mcp_tool()                                  # a tool for coding agents  (pip install faceplant[mcp])
async def get_bill(month: str) -> str: ...

svc.announce_wheel(target="faceplant-users",     # ship an extension wheel AT another service
                   wheel="plugin", entrypoint="billing_gate.plugin:register")

@svc.manifest                                    # compute the dashboard per request
def dash(user): return Stack(children=[...])

Escape hatches, always available: svc.app is a real FastAPI app (add WebSockets, SSE, middleware); svc.broker.request(key, data) / .publish(...); await svc.provision(fn) for blocking work off the DNS-critical event loop; @svc.background for startup loops; @svc.on_purge / @svc.gc_verdict to customize GC.

Testing is safe by default

Service only starts its heartbeat when a broker is actually configured (BROKER_URL set). A bare pytest or with TestClient(app) runs offline — no accidental registration against a live gateway. To test online behavior, use the in-process stub:

from fastapi.testclient import TestClient
from faceplant.testing import StubBroker, dev_jwt

def test_bills():
    with TestClient(svc.app) as client:                    # offline: no heartbeat
        r = client.get("/data", headers={"authorization": f"Bearer {dev_jwt('a@b.co')}"})
        assert r.status_code == 200

async def test_registration():
    stub = StubBroker(); stub.install(svc)
    await stub.beat(svc)
    assert "billing.total" in stub.responders

Docs

One page per concept in docs/: identity, widgets, plugin-host, providers, consumers & responders, mcp, gc, escape hatches, testing, and migrating from the template.

License

MIT.

Metadata

Release files for faceplant 0.3.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 faceplant 0.3.0
File Size Uploaded
faceplant-0.3.0.tar.gz 142.6 kB Details

Built distribution (wheel)

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

Total release size: 189.2 kB

Release files / faceplant-0.3.0.tar.gz

Download URL faceplant-0.3.0.tar.gz
Size 142.6 kB
Tags Source
SHA-256 checksum
How to use checksums
f889cdb1f585bdffdc34ff0ece32cfd36a2a4da8a15290aea74fcf2dc5c43e05
BLAKE2b-256 checksum
How to use checksums
025da991a12ba6a6fd0a4d8711b342dd59be75a946a3c2b96fdae19cf2f28937
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.6

Release files / faceplant-0.3.0-py3-none-any.whl

Download URL faceplant-0.3.0-py3-none-any.whl
Size 46.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2faa5a160531e9a8fac444560d824e9273e1f9bd85cbe8cc297d19af87c2df3e
BLAKE2b-256 checksum
How to use checksums
ec1a36594ec5e24b7079e352d0489725fb5ff1e6392746362220c0b6dad2cb25
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.6

Release history Release notifications | RSS feed

0.9.7

2 release files

0.9.6

2 release files

0.9.5

2 release files

0.9.4

2 release files

0.9.3

2 release files

0.9.2

2 release files

0.9.1

2 release files

0.9.0

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.7

2 release files

0.7.6

2 release files

0.7.5

2 release files

0.7.4

2 release files

0.7.3

2 release files

0.7.2

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

This release

0.3.0 This release

2 release files

0.2.0

2 release files

0.1.0

2 release files

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