Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.git
.github
.venv
node_modules
dist
uploads
attached_assets
android
test-results
playwright-report
.playwright
.env
*.tar.gz
*.apk
__pycache__
*.pyc
28 changes: 23 additions & 5 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

- name: Run provider contracts
run: |
uv run pytest -q tests/test_open_comp_prov_v0.0.0alpha.py tests/test_open_comp_rout_v0.0.0alpha.py tests/test_aone_open_comp_adap_v0.0.0alpha.py tests/test_a0_package.py
uv run pytest -q tests/test_open_comp_prov_v0.0.0alpha.py tests/test_open_comp_rout_v0.0.0alpha.py tests/test_aone_open_comp_adap_v0.0.0alpha.py tests/test_a0_package.py tests/test_publ_acce_poli_v0.0.0alpha.py
uv run python -m python.tests.contract_runner --only check_openai_compatible_registry_wiring --only check_openai_compatible_repair_regressions

check-console-tabs:
Expand Down Expand Up @@ -91,11 +91,17 @@
run: npm ci

- name: Install Python dependencies
run: uv sync --frozen --no-dev
run: uv sync --frozen

- name: Apply database schema
run: npm run db:push

- name: Check public request limits
run: node --import tsx scripts/chec_publ_rate_v0.0.0alpha.ts

- name: Run Python test suite
run: uv run pytest -q tests python/tests

- name: Boot Python backend
run: |
uv run uvicorn python.main:app --host 127.0.0.1 --port 8001 \
Expand All @@ -122,7 +128,18 @@
kill "$(cat /tmp/uvicorn.pid)" 2>/dev/null || true
fi

container-build:
name: Production container build
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v5

- name: Build production image
run: docker build --tag a0p:ci .

deployment-readiness:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
name: Google Cloud deployment readiness
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
Expand Down Expand Up @@ -152,7 +169,7 @@

deploy:
name: Build, push, deploy
needs: [provider-contracts, check-console-tabs, deployment-readiness]
needs: [provider-contracts, check-console-tabs, container-build, deployment-readiness]
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && vars.GCP_DEPLOY_ENABLED == 'true'
runs-on: ubuntu-latest
permissions:
Expand Down Expand Up @@ -195,6 +212,7 @@
--port=5000 \
--min-instances=0 \
--max-instances=10 \
--memory=512Mi \
--memory=1Gi \
--cpu=1 \
--set-secrets="DATABASE_URL=a0p-database-url:latest,SESSION_SECRET=a0p-session-secret:latest,XAI_API_KEY=a0p-xai-api-key:latest,DEEPSEEK_API_KEY=a0p-deepseek-api-key:latest,STRIPE_SECRET_KEY=a0p-stripe-secret-key:latest,STRIPE_WEBHOOK_SECRET=a0p-stripe-webhook-secret:latest"
--set-env-vars="APP_ENV=production,APP_ORIGIN=https://replit.interdependentway.org" \
--set-secrets="DATABASE_URL=a0p-database-url:latest,SESSION_SECRET=a0p-session-secret:latest,INTERNAL_API_SECRET=a0p-internal-api-secret:latest,ADMIN_EMAIL=a0p-admin-email:latest,XAI_API_KEY=a0p-xai-api-key:latest,DEEPSEEK_API_KEY=a0p-deepseek-api-key:latest,STRIPE_SECRET_KEY=a0p-stripe-secret-key:latest,STRIPE_WEBHOOK_SECRET=a0p-stripe-webhook-secret:latest"
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,7 @@ android/.gradle/
android/app/build/
android/build/
android/app/src/main/assets/public

# Runtime/user data must never enter source control.
/uploads/
/attached_assets/
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## Overview

**`a0`** is a 3-process autonomous AI-agent platform with a metadata-driven console UI. `a0` is the codebase/runtime; **`a0p`** is the deployed public instance ("a research instrument, not a product") at `replit.interdependentway.org`.
**`a0`** is a 3-process autonomous AI-agent platform with a metadata-driven console UI. `a0` is the codebase/runtime; **`a0p`** is the public-facing instance name ("a research instrument, not a product"). `PUBLIC_RELEASE.md` is the authority for whether an instance is ready to be described as live.

A single persistent agent — **ZFAE** (`a0(zeta fun alpha echo)`) — runs on the instrument. Large language models (Grok, GPT-5 mini, Gemini 2.5 Flash, Claude Sonnet) are treated as **energy providers**: they supply computational energy per response but are not the agent itself. Sub-agents (`a0(zeta{n})`) can fork the PCNA instance, run in parallel, and merge results back.

Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Grounding files: `README.md`, `package.json`, `pyproject.toml`.

## How access works for contributors

> Naming reminder: `a0` is this project / repository; `a0p` is the deployed instance of `a0` running publicly. See "Project name: `a0` vs `a0p`" in `README.md`.
> Naming reminder: `a0` is this project / repository; `a0p` is its public-facing deployment name. See `README.md` and `PUBLIC_RELEASE.md` before describing an instance as live.

The deployed instance (`a0p`) is open to read and use; donations do not unlock anything. You will not hit a paywall by signing up. However, a small set of write endpoints are owner-only because they mutate shared research-instrument state (agent state, learning state, system configuration). The full posture is described in `README.md` under "Access model" and the contract lives in `python/services/gating.py`.

Expand Down
35 changes: 29 additions & 6 deletions DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Overview

Every push to `main` automatically builds a Docker image and deploys to Cloud Run via GitHub Actions (`.github/workflows/deploy.yml`). Alternatively, use `cloudbuild.yaml` for a GCP-native trigger.
Every push to `main` runs release checks. After the explicit deployment opt-in is configured, GitHub Actions (`.github/workflows/deploy.yml`) builds the image and deploys it to Cloud Run.

---

Expand Down Expand Up @@ -75,6 +75,8 @@ values.
```bash
echo -n "postgres://..." | gcloud secrets create a0p-database-url --data-file=-
echo -n "your-session-secret" | gcloud secrets create a0p-session-secret --data-file=-
echo -n "your-separate-internal-api-secret" | gcloud secrets create a0p-internal-api-secret --data-file=-
echo -n "owner@example.com" | gcloud secrets create a0p-admin-email --data-file=-
echo -n "xai-key" | gcloud secrets create a0p-xai-api-key --data-file=-
echo -n "deepseek-key" | gcloud secrets create a0p-deepseek-api-key --data-file=-
echo -n "sk_live_..." | gcloud secrets create a0p-stripe-secret-key --data-file=-
Expand All @@ -84,7 +86,7 @@ echo -n "whsec_..." | gcloud secrets create a0p-stripe-webhook-secret --data-fil
Grant the service account access to each secret:

```bash
for SECRET in a0p-database-url a0p-session-secret a0p-xai-api-key a0p-deepseek-api-key a0p-stripe-secret-key a0p-stripe-webhook-secret; do
for SECRET in a0p-database-url a0p-session-secret a0p-internal-api-secret a0p-admin-email a0p-xai-api-key a0p-deepseek-api-key a0p-stripe-secret-key a0p-stripe-webhook-secret; do
gcloud secrets add-iam-policy-binding $SECRET \
--member="serviceAccount:$SA" \
--role="roles/secretmanager.secretAccessor"
Expand All @@ -98,11 +100,13 @@ Options:
- **Neon** (recommended for serverless): provision a database, copy the connection string into `a0p-database-url`
- **Cloud SQL**: add `--add-cloudsql-instances` to the `gcloud run deploy` command and use the Unix socket path

### 7. Auth provider (important)
### 7. Authentication

Replit Auth (OIDC) will not work outside Replit. Before going live on Cloud Run you must:
1. Add a Google OAuth 2.0 client ID in GCP Console → APIs & Services → Credentials
2. Swap the auth provider in `server/replit_integrations/auth.ts` to use `passport-google-oauth20`
a0p uses its repository-owned username/passphrase and PostgreSQL session flow;
it does not depend on Replit Auth. Set a unique production `SESSION_SECRET`,
keep `ADMIN_EMAIL` limited to the owner account, and verify registration,
sign-in, sign-out, and recovery against the production database before mapping
the public domain.

---

Expand Down Expand Up @@ -153,7 +157,26 @@ docker build -t a0p:local .
docker run -p 5000:5000 \
-e DATABASE_URL="..." \
-e SESSION_SECRET="..." \
-e INTERNAL_API_SECRET="a-different-random-secret" \
-e XAI_API_KEY="..." \
-e DEEPSEEK_API_KEY="..." \
a0p:local
```

### Public-access controls

The donation-funded public boundary is conservative by default and can be
tuned without code changes:

| Variable | Default | Purpose |
|---|---:|---|
| `APP_ORIGIN` | canonical hostname | Trusted origin for Stripe returns; set explicitly in production |
| `PUBLIC_PROVIDER_ALLOWLIST` | economical built-ins | Comma-separated provider IDs available to free users |
| `PUBLIC_GUEST_PROVIDER` | active provider | Optional economical provider pinned for the guest preview |
| `PUBLIC_MAX_PROVIDER_LANES` | `2` | Maximum provider calls in one free-tier orchestration or Fleet run |
| `PUBLIC_MODEL_REQUEST_LIMIT` | `24` | Authenticated model-starting requests per window |
| `PUBLIC_MODEL_WINDOW_SECONDS` | `3600` | Authenticated request-limit window |
| `GUEST_TOKEN_LIMIT` | `2000` | Conservative pre-reserved guest tokens per hour and IP |
| `AUTH_LOGIN_ATTEMPT_LIMIT` | `10` | Sign-in attempts per 15-minute window |
| `AUTH_SIGNUP_ATTEMPT_LIMIT` | `5` | Account-creation attempts per hour |
| `A0_PERSISTENT_UPLOADS_ENABLED` | unset | Show and accept chat attachments only after `uploads/` is durable shared storage |
21 changes: 19 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
FROM node:20-alpine AS builder
FROM ghcr.io/astral-sh/uv:0.11.18 AS uv

FROM node:20-bookworm-slim AS builder
WORKDIR /app

COPY package.json package-lock.json ./
Expand All @@ -7,17 +9,32 @@ RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine AS runner
FROM node:20-bookworm-slim AS runner
WORKDIR /app

RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates git \
&& rm -rf /var/lib/apt/lists/*

# The production unit owns both processes: Node/Express is public on :5000
# and launches the internal Python/Uvicorn child on :8001.
COPY --from=uv /uv /uvx /bin/
COPY package.json package-lock.json ./
RUN npm ci --omit=dev

COPY . .
COPY --from=builder /app/dist ./dist

ENV UV_LINK_MODE=copy
ENV UV_PYTHON_INSTALL_DIR=/opt/uv/python
RUN uv python install 3.12 \
&& uv sync --frozen --no-dev

RUN mkdir -p uploads

ENV NODE_ENV=production
ENV PYTHONUNBUFFERED=1
ENV PATH="/app/.venv/bin:$PATH"
ENV PORT=5000
EXPOSE 5000

Expand Down
46 changes: 46 additions & 0 deletions PUBLIC_RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# a0p public release gate

a0p is not considered publicly released merely because a hostname or repository
exists. A release is healthy only when the canonical GitHub-to-Cloud-Run path
passes the checks below.

## Implemented in the public-readiness slice

- One production image runs the Express boundary and its Python/Uvicorn child.
- Production refuses a missing internal API secret.
- Login, registration, guest chat, and authenticated model starts have durable
or atomic abuse limits.
- Free inference is restricted to a configurable economical-provider set and
at most two provider calls per orchestration or Fleet run.
- CLI model access remains working-set/operator-only.
- Tool tiers are enforced at model exposure and dispatch; shared-state,
account-backed, shell, recursive, and high-cost tools are not public.
- Public request bodies are bounded and backend exception detail is not returned.
- Security headers are enabled, dependency audit is clean, and runtime uploads
are excluded from source and container builds.

## Must pass before mapping the public domain

1. Purge historical runtime uploads and attachments from Git history, review the
affected material without redisclosing it, and rotate any credential that
might have appeared there. Removing files from the current tree is not a
historical purge.
2. Configure the Cloud Run service, PostgreSQL database, Secret Manager values,
and GitHub deployment opt-in described in [DEPLOYMENT.md](DEPLOYMENT.md).
3. Build the Docker image in CI, apply the database schema, run the public rate
check, and smoke-test registration, session rotation, guest chat, signed-in
chat, provider denial, and cross-user conversation denial against staging.
4. Keep chat attachments disabled or visibly unavailable until `uploads/` is
backed by durable shared storage. A Cloud Run container filesystem is
ephemeral and cannot be treated as user storage.
5. Do not expose system-primary memory or cross-owner matching until the
executable privacy, consent, audit, export, correction, retention, and
deletion contracts in `docs/replit-backend-foundation.md` exist and pass.
6. Map the public hostname only after the staging revision is healthy, then
verify TLS, security headers, error handling, and the Stripe webhook.

## Release evidence

Record the deployed commit SHA, Cloud Run revision, migration result, smoke-test
result, and rollback revision in the release issue. If any item above is not
true, describe a0p as a release candidate rather than a live public service.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# a0p — a research instrument

**a0p is a research instrument, not a product.** It is the deployed instance of `a0` (this codebase) running publicly at [replit.interdependentway.org](https://replit.interdependentway.org). It explores agent / energy-provider / PCNA dynamics in the open. Anyone may read and use it. Code-altering access is restricted to the owner and a small set of explicitly-invited collaborators. The instrument is funded by donations; it does not solicit subscribers.
**a0p is a research instrument, not a product.** It is the public-facing instance of `a0` (this codebase), now being prepared for a controlled public release. It explores agent / energy-provider / PCNA dynamics in the open. Public use remains free; code-altering access is restricted to the owner and a small set of explicitly invited collaborators. The instrument is funded by donations and does not solicit subscribers. See [PUBLIC_RELEASE.md](PUBLIC_RELEASE.md) for the live-release gate instead of assuming that a historical hostname is healthy.

> **Naming:** `a0` = the project / runtime / repository (used in contributor-facing material). `a0p` = the deployed instance of `a0` (used in user-facing UI copy and billing). The thing you build is `a0`; the thing that runs is `a0p`.

Expand Down Expand Up @@ -52,7 +52,7 @@ The frontend has zero hardcoded tabs. Every Python route module declares `UI_MET

## Access Model

- **Reading and using the app is free for everyone.** Every tab is open. There is no paywall and donations do not unlock anything.
- **Public use is free.** Donations do not unlock instrument capabilities. Public routes are bounded for privacy, abuse, and provider cost; shared-state mutation remains closed.
- **Operator tier** — `@interdependentway.org` accounts are auto-promoted to `ws` on login.
- **Owner-only ("admin") write endpoints** govern actions that mutate shared instrument state: agent state, learning state, system configuration, and module toggles. Per-user CRUD on your own data is not admin-gated. The static contract lives in `python/tests/contracts/gating.py`.

Expand All @@ -66,7 +66,7 @@ a0p runs on donations. There is no subscription tier and no perk unlocked by don

> "I don't have the cash required for 501c3 status, so I have to report it for taxes, but every tax payer is allowed to claim up to five hundred dollars in charitable donations per year without receipts required."

To donate, visit [a0p/pricing](https://replit.interdependentway.org/pricing). Minimum $5.
When the public deployment is healthy, its Pricing page accepts donations. Minimum $5.

The only productized service is the **EDCMbone transcript explainer** — a one-off paid analysis ($50 for 3 explanations, ~$16.67 each) priced against the operator's $1,000/hr benchmark.

Expand Down
37 changes: 23 additions & 14 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
# Security Policy

## Supported Versions
## Supported code

Use this section to tell people about which versions of your project are
currently being supported with security updates.
Security fixes target the current `main` branch and any active public a0p deployment.
Older commits, forks, local installations, and retired releases are not
independently supported.

| Version | Supported |
| ------- | ------------------ |
| 5.1.x | :white_check_mark: |
| 5.0.x | :x: |
| 4.0.x | :white_check_mark: |
| < 4.0 | :x: |
## Report a vulnerability privately

## Reporting a Vulnerability
Email `wayseer@interdependentway.org` with the subject `a0p security report`.
Please do not open a public issue for an unpatched vulnerability or include
credentials, private conversation content, or personal data in GitHub.

Use this section to tell people how to report a vulnerability.
Include the affected URL or component, reproduction steps, likely impact, and
the least-sensitive proof needed to verify the report. Do not access another
person's data, execute destructive actions, or incur provider charges while
testing.

Tell them where to go, how often they can expect to get an update on a
reported vulnerability, what to expect if the vulnerability is accepted or
declined, etc.
We aim to acknowledge a report within three business days and provide a status
update within seven business days. Confirmed issues are prioritized by impact;
credit is offered when requested and when disclosure does not create additional
risk.

## Public-data incidents

If source control, a build artifact, or a deployment exposes user-generated
content, report it through the same private address. Treat the affected data as
compromised until it has been removed from the live surface and repository
history and any exposed credentials have been rotated.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed attached_assets/a0p_v1.0.0_bundle_1772150205840.zip
Binary file not shown.
Loading