Skip to main content

Web App

Service lifecycle framework with FastAPI background services, dependency management, and optional desktop GUI launcher.

Overview

vcti-webapp provides a Service base class for managing background services with ordered dependencies and thread-safe lifecycle control. FastAPIService extends this to run FastAPI applications via uvicorn in background threads with automatic port discovery. For desktop applications, the optional GUI module adds AppLauncher (pywebview window), StaticDirApp (static file serving), and ZippedApp (serve from ZIP archives).

Installation

Core (services only):

pip install vcti-webapp>=1.0.0

With desktop GUI support:

pip install vcti-webapp[gui]>=1.0.0

With ZIP archive support (includes GUI + vcti-session):

pip install vcti-webapp[zip]>=1.0.0

Quick Start

Background FastAPI service

from fastapi import FastAPI
from vcti.webapp import FastAPIService

app = FastAPI()

@app.get("/health")
async def health():
    return {"status": "ok"}

# Start in background thread with auto-discovered port
with FastAPIService(app, name="api") as svc:
    print(f"Running on http://localhost:{svc.port}")
    # ... do work ...
# Server stops automatically on exit

Service dependencies

from vcti.webapp import Service, FastAPIService, service_dependency

class DatabaseService(Service):
    def _start_service(self):
        self.connection = connect_to_db()
    def _stop_service(self):
        self.connection.close()

db = DatabaseService(name="database")
api = FastAPIService(app, name="api")
api.add_dependency(db)

api.start()   # starts db first, then api
api.stop()    # stops api first, then db (reverse order)

Error recovery

from vcti.webapp import CyclicDependencyError

# Circular dependencies are caught at registration time
try:
    svc_a.add_dependency(svc_b)
except CyclicDependencyError:
    print("Would create a cycle — dependency not added")

# Start failures roll back dependencies automatically
try:
    svc.start()
except RuntimeError:
    # All already-started dependencies have been stopped
    print("Service failed to start, deps cleaned up")

Desktop GUI (requires pywebview)

from vcti.webapp.app_launcher import AppLauncher

launcher = AppLauncher(app, window_title="My App", window_size=(1280, 720))
launcher.start()  # opens desktop window, blocks until closed

Core API

Service (base)

Method Description
start() Start service and dependencies (thread-safe, safe to call from multiple threads)
stop() Stop service and optionally dependencies
add_dependency(service) Register a dependency (returns self, raises CyclicDependencyError on cycle)
is_running() Check if running

FastAPIService

Parameter Default Description
app (required) The FastAPI application
host "127.0.0.1" Bind address (loopback only by default for security)
port None TCP port (None = auto-discover a free port)
ready_timeout 10.0 Seconds to wait for server readiness; 0 to skip
ready_poll_interval 0.05 Seconds between readiness polls
uvicorn_config None Extra kwargs forwarded to uvicorn.Config

GUI classes (optional — vcti-webapp[gui])

Class Purpose
AppLauncher Desktop window via pywebview
StaticDirApp Serve static files from a directory
ZippedApp Extract and serve from ZIP archive

Advanced Usage

Custom uvicorn configuration

# Suppress noisy startup logs
svc = FastAPIService(app, uvicorn_config={"log_level": "warning", "access_log": False})

# TLS termination at uvicorn
svc = FastAPIService(app, uvicorn_config={
    "ssl_keyfile": "key.pem",
    "ssl_certfile": "cert.pem",
})

Slow CI environments

# Increase readiness timeout on slow machines
svc = FastAPIService(app, ready_timeout=30.0, ready_poll_interval=0.1)

Shared dependencies

When multiple services share a dependency, use stop_dependencies=False to prevent the first service to stop from tearing down the shared resource:

db = DatabaseService(name="db")

api = FastAPIService(app, name="api", stop_dependencies=False)
api.add_dependency(db)

worker = WorkerService(name="worker", stop_dependencies=False)
worker.add_dependency(db)

# api.stop() leaves db running — worker still needs it

Static file serving without GUI

StaticDirApp inherits from AppLauncher (which requires pywebview), but you can serve static files without a GUI using plain FastAPIService:

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from vcti.webapp import FastAPIService

app = FastAPI()
app.mount("/static", StaticFiles(directory="./dist"), name="static")

with FastAPIService(app, name="docs-server") as svc:
    print(f"Serving at http://localhost:{svc.port}/static/index.html")

Root redirect (show_index_on_root)

StaticDirApp and ZippedApp redirect GET / to {static_route}/{default_page} by default. Set show_index_on_root=False if you define your own root route or want / to return 404:

StaticDirApp(Path("./dist"), show_index_on_root=False)

Threading

start() and stop() are thread-safe. Calling start() from multiple threads simultaneously is safe — only one thread runs startup; others return immediately. See docs/design.md for details on lock ordering and concurrency guarantees.


Dependencies

Metadata

Release files for vcti-webapp 1.0.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 vcti-webapp 1.0.0
File Size Uploaded
vcti_webapp-1.0.0.tar.gz 18.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for vcti-webapp 1.0.0
File Interpreter ABI Platform
vcti_webapp-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 31.5 kB

Release files / vcti_webapp-1.0.0.tar.gz

Download URL vcti_webapp-1.0.0.tar.gz
Size 18.1 kB
Tags Source
SHA-256 checksum
How to use checksums
af1f6a1aa85870da1746e186ab7ec1304fb7a5a58e3a68588af84f0437d76260
BLAKE2b-256 checksum
How to use checksums
16a03356626e70e377e8c1a6612728252900b37f6fbe45a27d48836eca04d566
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

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 Mar 29, 2026.

Transparency log

Release files / vcti_webapp-1.0.0-py3-none-any.whl

Download URL vcti_webapp-1.0.0-py3-none-any.whl
Size 13.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5c3ee45fc9908f2f8396a71acf57b1a6ac748698b5f840979a67b8e8866fd26a
BLAKE2b-256 checksum
How to use checksums
c0f2e86255f83aba85a98763709f91c35ed071616e4b74ab4c1112f132c16223
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

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 Mar 29, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.0 This release

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