make something.
phast.
- Bring the intelligence. Phast turns it into something you can use.
-diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..976a37f --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,21 @@ +# AGENTS.md + +## Scope + +Build and maintain Phast app as a deliberately small Python + Prefab + MCP application platform. + +## Product model + +- `my.phast.app` is the owner control plane. +- Each `app.phast.app` is independently useful and independently connectable. +- `/` is the human Prefab UI by default. +- `/mcp` is the agent interface by default. +- A Prephab is a cloneable Python + Prefab + MCP application shape. + +## Working rules + +- Prefer boring, explicit Python over framework sprawl. +- Keep one source of truth for app state and capabilities. +- Do not fake auth or security; mark seams explicitly. +- No delete path until it is deliberately designed. +- Commit small coherent changes to `main` and leave the system simpler. diff --git a/Dockerfile b/Dockerfile index 1ba7ab4..093f3c9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,10 +3,10 @@ ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 WORKDIR /app COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt -COPY server.py registry.py index.html phast.json ./ +COPY server.py registry.py index.html phast.json start.sh ./ +COPY my/index.html my/index.html ARG SOURCE_SHA=unknown ENV SOURCE_SHA=$SOURCE_SHA -RUN useradd --create-home --uid 10001 phast -USER phast +RUN useradd --create-home --uid 10001 phast && chmod +x start.sh EXPOSE 8080 -CMD ["python", "server.py"] +CMD ["./start.sh"] diff --git a/README.md b/README.md index 19f414e..1e80fd8 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,73 @@ -# Phast runtime +# Phast app -This directory is the canonical source for the existing `phast-app` Fly app, -serving `phast.app` and `my.phast.app`. It remains one Python/FastMCP runtime. +Canonical source of truth for Phast app. -## App convention +> **Connect your tools once. Bring your tool belt anywhere.** -`phast.json` is the minimal Prephab manifest. Schema version 1 requires a -stable slug and name, an HTTPS origin, Python as the runtime, `/` as the default -human route, `/mcp` as the default MCP route, and Prephab shape version 1. -Registry tools return this same shape. +Phast app gives a person one portable MCP plus a programmable Prefab home. Connect `my.phast.app/mcp` to an agent to create and manage Phast apps. A created Phast app has two matching surfaces: -`my.phast.app` renders its human surface with the installed Prefab UI package. -Its MCP exposes `apps_list`, `apps_get`, `apps_create`, and `apps_update`, plus -bounded status and Prefab-app tools. There is intentionally no delete. +- `user.phast.app` — human / Prefab home +- `user.phast.app/mcp` — the user's portable MCP tool belt -## Production blockers +## V0 contract -The current Fly app has no volume and Phast has no accepted owner-auth pattern. -The registry therefore refuses to treat the image filesystem as durable, and -create/update always fail before writing. Do not deploy this as an enabled -control plane until both seams are closed: +`phast.json` is deliberately boring. It is mostly: -1. create and attach one Fly volume at `/data`, then set - `PHAST_DATA_DIR=/data`; the runtime verifies that the directory is a mount; -2. reuse an accepted FastMCP owner identity provider and replace the explicit - mutation guard with caller identity/authorization checks. +1. the MCP tool list and provider connections; +2. references to delegated credential authority (Persimmons, never raw secrets); +3. Prefab home/widget settings. -Do not substitute a shared secret in the UI or an environment flag for owner -authentication. Once both prerequisites exist, exercise list/get/create/update -through `my.phast.app/mcp`, verify persistence across a Machine replacement, -then deploy through the existing Phast launch path. +The browser and chat are two editors for the same declaration. -## Local validation +### Browser flow -Run `python -m unittest tests.test_phast`. Tests use a temporary SQLite file; -they do not weaken the production mount or authentication guards. +Home → Edit Home → Add Widget → provider search → connect if necessary → place/configure widget. -## On Teahub prod (2026-10-14) -Ported from the old factory (`tea-mcp/src/phast`, plus `phastacp` as `acp/`) as it was. Phast is a client of Teahub: -this org owns the code, the site (`phast/site`) and phast.app. The Fly app named in `fly.toml` (`phast-app`) does not -exist yet; the production blockers above still stand (volume at /data, owner auth via Gitea OAuth). +### Agent flow + +Connect once to `my.phast.app/mcp` → create/manage `user.phast.app` → search providers → add tools/widgets → stream/taste-test Prefab in chat → persist accepted UI to the same `phast.json`. + +## Boundaries + +- **Phast app composes.** It owns the portable MCP/tool belt and Prefab composition. +- **Oracle discovers.** Provider/MCP/OAuth discovery comes from the capability graph, not a hard-coded Phast app directory. +- **Persimmons holds secrets.** Phast app stores connection/authority references, not secret values. +- **Factory builds/operates.** Provisioning uses bounded deterministic infrastructure routes. +- **Clients are replaceable.** ChatGPT, Claude, Codex, or another MCP client should be able to use the same Phast app without recollecting the user's tools. + +V0 is intentionally not arbitrary app generation. Prove the portable-tool-belt + editable-home loop first. + +## Naming and copy + +Use **Phast app** when naming the product or one of its instances; use +**Phast apps** for the plural. Write “your Phast app,” “create a Phast app,” +and “My Phast app” for the management connector. Existing all-caps display +styles may use “PHAST APP.” Apply this to page titles, metadata, navigation, +buttons, examples, generated homes, MCP display text, and documentation. + +Keep deliberate substitutions for “fast”: “make something phast,” +“built phast,” and “make MCP apps phast.” Do not rename literal domains, +URLs, `phast.json`, environment variables, CSS classes, tool names, or +schema keys/values as part of a copy edit. Preserve user-chosen app names. + +The public copy lives in `index.html` (phast.app), `my/index.html` +(my.phast.app), and `server.py` (generated personal homes and MCP display +text). `phast.json` supplies the management display name. Review these +surfaces together when changing product naming. + +Chair review: the standalone “PHAST.” wordmark and the “My Phast” connector +label could be separate brand treatments. This change proposes “PHAST APP.” +and “My Phast app” consistently; the chair should confirm those treatments. +The phrase “your Phast” names an app instance, so it becomes “your Phast app.” + +## Running + +Prod since 2026-10-14: Fly app `phast-app` (this `fly.toml`), one machine, volume `phast_data` at `/data` +(`PHAST_DATA_DIR=/data/phast`, `FASTMCP_HOME=/data/fastmcp`). It serves phast.app, my.phast.app and +`*.phast.app`; the two pages are this repo's `index.html` and `my/index.html`, not copies. Sign-in is +Teahub OAuth (app "Phast app" owned by `teahubxyz`, redirect `https://my.phast.app/auth/callback`); +`GITEA_OAUTH_CLIENT_ID`, `GITEA_OAUTH_CLIENT_SECRET` and `JWT_SIGNING_KEY` exist only as Fly secrets. +`ALLOWED_GITEA_USERS` says who may use it. + +Deploy: `fly deploy --build-arg SOURCE_SHA=$(git rev-parse HEAD)`; `GET /healthz` reports the SHA. +History before 2026-10-14 is `matcha/phast` from the old factory, merged in. diff --git a/__init__.py b/__init__.py index ef13255..67015ff 100644 --- a/__init__.py +++ b/__init__.py @@ -1 +1 @@ -"""Phast application runtime.""" +"""Phast app runtime.""" diff --git a/fly.toml b/fly.toml index 7564318..a1f1072 100644 --- a/fly.toml +++ b/fly.toml @@ -4,20 +4,31 @@ primary_region = "iad" [build] dockerfile = "Dockerfile" +[env] + GITEA_URL = "https://teahub.xyz" + PHAST_DATA_DIR = "/data/phast" + FASTMCP_HOME = "/data/fastmcp" + ALLOWED_GITEA_USERS = "matcha,chai" + PORT = "8080" + PHAST_HOST = "0.0.0.0" # asyncio binds :: as IPv6-only; the Fly proxy arrives over IPv4 + [http_service] internal_port = 8080 force_https = true - auto_stop_machines = "stop" - auto_start_machines = true + auto_stop_machines = "off" min_machines_running = 1 [[http_service.checks]] - grace_period = "15s" - interval = "15s" + grace_period = "20s" + interval = "30s" method = "GET" path = "/healthz" timeout = "5s" +[mounts] + source = "phast_data" + destination = "/data" + [[vm]] memory = "512mb" cpu_kind = "shared" diff --git a/index.html b/index.html index 99fa4ef..6c29397 100644 --- a/index.html +++ b/index.html @@ -1,73 +1,46 @@
- - - - -Bring the intelligence. Phast turns it into something you can use.
-MCP APPS // ZERO TO USEFUL
+Connect your tools once.
Bring your tool belt anywhere. ChatGPT, Claude, Codex — same Phast app, same tools, same home.