forked from retoor/devplacepy
Add community node protocol spec, Agent Skills, and make the failing tests hermetic
Community nodes (Phase 1 of plan.md): the protocol specification in services/nodes/spec.py as the single source of truth, GET /nodes/spec.json, a generated protocol docs page, the Community nodes API docs group, and the implementation plan plus the residential proxy research. The no-open-proxy rule is enforced in the spec (open_proxy cannot be set). Agent Skills: register the Skills API docs group, cascade skills on account deletion, add the nested CLAUDE.md, README and routes docs, and cover the parser, the fetcher (end to end against a mock origin), the data layer and the routes with 73 unit and 16 api tests. Tests made hermetic with the repo's existing patterns instead of ambient environment: embeddings tests set OPENROUTER_API_KEY via monkeypatch, the workspace tests install FakeBackend via runtime.set_backend, and the skills install tests use run_async. DockerCliBackend now returns 127 (command not found) when the CLI is missing instead of raising FileNotFoundError, so a host without docker answers with a clean error rather than a 500. The Providers tab empty state no longer names the Model routes tab. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -64,6 +64,8 @@ devplacepy/
|
||||
| `/auth` | Signup, login, logout, forgot/reset password |
|
||||
| `/feed` | Post feed with topic/tab filtering and free-text `search` (title, content, and author username) in the left panel (public). Each page interleaves authors so no two consecutive posts share an author. |
|
||||
| `/topics` | Crawlable per-topic category pages (public): `/topics` hub links every topic with a live post count, `/topics/{topic}` lists that topic's posts with its own canonical URL, title, and breadcrumbs |
|
||||
| `/skills` | Per-user Agent Skills: list, detail, install from a URL, enable/disable, delete. Private to the owner, `noindex` |
|
||||
| `/nodes` | Community node network. Today only the public protocol specification `GET /nodes/spec.json` exists; the socket, devices, consent and clients are planned (see `plan.md`) |
|
||||
| `/news` | Developer news listing, detail page with comments |
|
||||
| `/posts` | Post detail, creation |
|
||||
| `/gists` | Code gist listing, detail, creation, and editing; left panel offers language filtering and free-text `search` (title, description, and author username), public read. A plain Markdown gist has a **View rendered / View raw** toggle beside its Copy button, switching between the raw source and the same rendered view a `Markdown Rendered` gist always shows |
|
||||
@@ -246,6 +248,16 @@ DevPlace is open in the sense that it does not editorialise technical opinion. I
|
||||
- **Account deletion.** Self-service at `/profile/{username}/delete`, reauthenticated with the account password. Sessions and tokens are revoked, the profile is anonymised immediately, and all content is removed under one deletion event that stays restorable for `account_deletion_grace_hours` (default 24) before `devplace accounts prune` (and the Moderation housekeeping service) purges it permanently. The devRant `DELETE /api/users/me` routes into the same cascade.
|
||||
- **Legal pages**: [Terms of Service](/docs/terms.html), [Community Guidelines](/docs/community-guidelines.html), [Privacy Policy](/docs/privacy.html), [How moderation works](/docs/content-moderation.html), [Notice and takedown](/docs/intellectual-property.html) and [Contact](/docs/contact.html). All six are indexed in the sitemap and reachable from the docs index; the footer of every page links Terms, Privacy, Community Guidelines and Contact. Contact details come from the `contact_email`, `contact_phone` and `contact_address` settings, so the in-product page and any app-store trader declaration have one source of truth.
|
||||
|
||||
## Skills
|
||||
|
||||
Members can install [Agent Skills](https://agentskills.io) from a URL at `/skills` so Devii can use them. A skill is a `SKILL.md` (YAML frontmatter plus markdown instructions) with optional text files under `scripts/`, `references/` and `assets/`. Skills are private to their owner, count against a per-user cap, and only the name and a one-line description of enabled skills are added to Devii's system prompt; Devii loads the full text on demand. A skill is untrusted third-party text: it can never grant Devii a tool, bypass a confirmation or run a script. Deleting a skill is a soft delete and needs confirmation when Devii does it. Documented in the API docs under *Skills* and in `devplacepy/services/skills/CLAUDE.md`.
|
||||
|
||||
## Community nodes (planned, specification only)
|
||||
|
||||
Members will be able to opt in to donate a little of their connection to the DevPlace community, so search can reach sites that block datacenter addresses. **Today only the specification exists:** `GET /nodes/spec.json` and the [Node network protocol](/docs/nodes-protocol.html) docs page, both generated from `devplacepy/services/nodes/spec.py`. No account can donate yet and nothing connects.
|
||||
|
||||
What is fixed already, because the specification enforces it: nobody is enrolled by default; a node is not an open proxy and only ever contacts a short published allowlist of websites, on ports 80 and 443, never a private address; the refusal is enforced on the donor's machine as well as on the server; every limit is a published hard cap. The implementation plan, the measured and unmeasured claims, and the legal gate are in `plan.md`.
|
||||
|
||||
## Vibe coding (Alpha, admin only)
|
||||
|
||||
Build software by talking to an AI agent instead of typing every line. Create a project for storage, attach a container to it (the shared `ppy` image, your files mounted at `/app`), start it, and open a terminal. The whole flow is drivable conversationally through Devii. Inside every container three agents ship preinstalled and run on **your own API key**, so all AI usage is metered to your account: **DevPlace Code (`dpc`)**, a coding agent in the same class as Claude Code; **`botje.py`**, a plug-and-play DevPlace bot you can copy and customise; and **`pagent`**, a minimal zero-dependency agent. Each container is launched with `DEVPLACE_BASE_URL`, `DEVPLACE_OPENAI_URL`, `DEVPLACE_API_KEY`, `DEVPLACE_USER_UID`, `DEVPLACE_CONTAINER_NAME`, `DEVPLACE_CONTAINER_UID`, and `DEVPLACE_INGRESS_URL` already set. Publish a container port to a public URL at `/p/<slug>` by setting an `ingress_slug` and `ingress_port` (ask Devii to do it at create time). The feature is in **Alpha** and currently limited to administrators; the full walkthrough, including a tutorial that vibes a web app and puts it online, is at `/docs/getting-started-vibing.html`.
|
||||
@@ -311,9 +323,13 @@ accepts three header-based methods, resolved centrally in `get_current_user`
|
||||
Every user has an `api_key` (a uuid7), set at signup and backfilled for existing
|
||||
users on startup (and via `devplace apikey backfill`). The key is shown on the
|
||||
profile page to its owner (and to admins for any user) with a live **Regenerate**
|
||||
button; regenerating invalidates the old key immediately. Invalid credentials
|
||||
return `401`; requests with no credentials behave as anonymous (browser redirect to
|
||||
login). Full details and copy-paste `curl` examples live at `/docs/authentication.html`.
|
||||
button; regenerating invalidates the old key immediately. A successful JSON
|
||||
`POST /auth/login` or `POST /auth/signup` also returns the account's `email`,
|
||||
`api_key`, and `is_admin` flag, and `GET /profile/{username}` as JSON reports
|
||||
`profile_is_admin` plus the key (null unless the viewer owns the profile or is
|
||||
an admin). Invalid credentials return `401`; requests with no credentials behave
|
||||
as anonymous (browser redirect to login). Full details and copy-paste `curl`
|
||||
examples live at `/docs/authentication.html`.
|
||||
|
||||
```bash
|
||||
curl -H "X-API-KEY: <your-key>" https://your-host/notifications
|
||||
@@ -329,8 +345,9 @@ website does is automatable from the same URLs. A request gets JSON when it send
|
||||
`Accept: application/json` or `Content-Type: application/json`; a normal browser navigation
|
||||
(`Accept: text/html`) always gets HTML, so existing behaviour is unchanged (the legacy
|
||||
`X-Requested-With: fetch` AJAX header still drives the four engagement endpoints only). JSON responses are defined by Pydantic models in `devplacepy/schemas/` and built
|
||||
from the same context the templates use (sensitive user fields like `email`/`api_key`/
|
||||
`password_hash` are never exposed). Page GETs return the page payload; form actions return a
|
||||
from the same context the templates use (`password_hash` is never exposed; `email` and
|
||||
`api_key` are exposed only to the account owner or an admin, never to third parties).
|
||||
Page GETs return the page payload; form actions return a
|
||||
uniform envelope `{ "ok": true, "redirect": "…", "data": {…} }`; errors return
|
||||
`{ "error": { "status", "message" } }` (validation → `422` with a `fields` map, unauthenticated
|
||||
JSON → `401`, non-admin → `403`). The core lives in `devplacepy/responses.py`
|
||||
@@ -1185,6 +1202,17 @@ Open `http://<host>:${PORT}` (default 10500). `make docker-logs` tails output; `
|
||||
|
||||
`make docker-build` and `make docker-up` always apply the `docker-compose.containers.yml` overlay, so the admin-only Container Manager works with no extra setup. Mounting the docker socket grants the app **root on the host**; the feature itself stays disabled until an admin enables **Containers** on `/admin/services` (and the `ppy` image is built once with `make ppy`), but treat the whole deployment as trusted-admins-only. Always update through the make targets - a plain `docker compose up -d` drops the overlay and silently removes the socket and CLI.
|
||||
|
||||
### Podman (rootless) hosts
|
||||
|
||||
Hosts without a docker daemon run the same stack via `podman-compose` plus the `docker-compose.podman.yml` overlay - see [PODMAN.md](PODMAN.md) for the full notes. Short version:
|
||||
|
||||
```bash
|
||||
make podman-build # build the images
|
||||
make podman-up # start (warns if user linger is off)
|
||||
```
|
||||
|
||||
The base compose file runs the app as `user: 1000:1000`, which is wrong under rootless podman (container uid 1000 maps to a subordinate host uid that cannot write the bind-mounted checkout, so the app crashes with `PermissionError` and nginx never starts). The overlay runs the app as container root instead, which maps back to the invoking host user and stays unprivileged on the host. Always use the `make podman-*` targets - a bare `podman-compose up -d` drops the overlay and recreates the crashing container. Never mix `podman-compose` with native `podman compose` in this directory (different container names, they fight over port 10500). Reboot survival needs both `restart: unless-stopped` (already set) and systemd user linger (`sudo loginctl enable-linger <user>`); `make podman-up` warns when linger is off.
|
||||
|
||||
### Updating
|
||||
|
||||
```bash
|
||||
|
||||
Reference in New Issue
Block a user