# Get started with vibing > **Alpha, admin-only.** Vibe coding is a preview feature. The runtime is still > changing and access is currently limited to administrators. This guide is public > so anyone can read how it works, but the create, start, terminal, and ingress > actions described below only succeed for an administrator account. **Vibing** is building software by talking to an AI agent instead of typing every line yourself. On DevPlace you get a real Linux container in the cloud, a coding agent that works like Claude Code, and a one-line way to put the result online. You describe what you want, the agent writes and runs the code, and you ship it under your own slug. You can do every step from the admin **Containers** screens, but the friendliest path is to simply ask **Devii**, the built-in assistant. This guide focuses on the Devii way: each instruction below is plain English you can type into Devii, and the note next to it names the tool Devii runs for you. ## What you get - A **project** to hold your files (the persistent storage for your work). - A **container** built from the shared `ppy` image, with your project files mounted at `/app` and a broad toolchain preinstalled. - Three AI agents baked into every container, all running on **your own API key**: - **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. - **`pagent`** - a minimal, zero-dependency agent for small scripted tasks. - **Ingress**: publish a port from your container to a public URL at `/p/`. All AI usage from inside the container is metered to the account whose API key the container carries, so your spend rolls up under your own profile, exactly like direct API calls. ## The four steps, the Devii way Open Devii from the user menu (the **Devii** item) and type these in order. Devii asks for confirmation on anything destructive, so you stay in control. **1. Create a project (storage).** > "Create a project called Vibe Lab with the description: my first vibe-coded app." Devii calls `create_project`. The project is the home for every file your container produces. **2. Attach a container to it.** > "In Vibe Lab, create a container named lab that maps port 8000." Devii calls `container_create_instance`. The instance runs the shared `ppy` image with your project mounted at `/app`. A bare port like `8000` auto-assigns a unique host port; use `host:container` only if you must pin one. **3. Start the container.** > "Start the lab container in Vibe Lab." Devii calls `container_instance_action` with `action=start`. (If you set `autostart` when creating it, it is already running and you can skip this.) **4. Open a terminal.** > "Open a terminal in the lab container." Devii calls the `open_terminal` action, which opens a floating xterm.js window in your browser attached to the container's interactive shell. From here you run `dpc`, `botje.py`, or anything else. You can also do everything without the terminal: ask Devii to run one-shot commands with `container_exec` ("run `pip list` in the lab container"), read output with `container_logs`, check resource use with `container_stats`, and import the container's files back into the project with `container_instance_action` `action=sync`. ## Inside the container - The OS user is always **`pravda`** (uid 1000). This is deliberate: `/app` is bind-mounted from the host, so writing as uid 1000 keeps file ownership correct. - Your working directory is **`/app`**, which is your project's files. Anything you create there can be synced back into the project. - **`apt` and `sudo` work without real root.** `apt install ` installs system packages through a fakeroot wrapper, and `sudo` runs the command as `pravda` rather than switching to root. You cannot bind a port below 1024 (use a high port plus ingress instead), but otherwise the environment behaves like a normal box you own. - Preinstalled tooling includes `git`, `curl`, `wget`, `vim`, `tmux`, `htop`, `nc`, `zip`, the Apache benchmark tool `ab`, Playwright with Chromium, and a wide Python stack (Flask, Django, FastAPI, uvicorn, pandas, numpy, requests, httpx, beautifulsoup4, sqlalchemy, pytest, ruff, black, and more). ## DevPlace Code (dpc) `dpc` is **DevPlace Code**, a terminal coding agent that provides the same kind of experience as Claude Code: you give it a task, it reads and writes files, runs commands, fixes what it broke, and iterates until the job is done. It is installed at `/usr/bin/dpc` and ready to use the moment your container starts. ```bash dpc "build a small FastAPI app in app.py that serves a JSON health check at /" ``` `dpc` reads your API key from the container environment (`DEVPLACE_API_KEY`) and talks to the platform AI gateway, so **all of its AI usage is metered through your own account**. There is no separate key to manage and nothing to configure: it is plug and play. In a **workspace** you do not even have to start it. The DevPlace editor opens a **DevPlace Code** terminal running `dpc` for you the moment the workspace boots, with a plain shell beside it. See [The workspace editor](/docs/workspace-editor.html) for the boot terminals, the trust policy, and every size you can change. ## Container environment keys Every container is launched with these variables already set. Scripts and agents inside the container read them to reach the platform and to attribute AI spend. | Variable | What it contains | |----------|------------------| | `DEVPLACE_BASE_URL` | The public base URL of this DevPlace instance. | | `DEVPLACE_OPENAI_URL` | The AI gateway endpoint, `DEVPLACE_BASE_URL` + `/openai/v1`. | | `DEVPLACE_API_KEY` | The API key used for every AI call. Spend is metered to this account. | | `DEVPLACE_USER_UID` | The DevPlace user id whose identity the container carries. | | `DEVPLACE_CONTAINER_NAME` | The instance's name. | | `DEVPLACE_CONTAINER_UID` | The instance's unique id. | | `DEVPLACE_INGRESS_URL` | The public URL of this container when ingress is set, otherwise empty. | The key that lands in `DEVPLACE_API_KEY` is resolved in order from the instance's **run-as user**, then its creator, then the project owner. You can point a container at a specific account by asking Devii to set `run_as_uid` when creating or configuring it. The OS user stays `pravda`; only the identity and key change. ## botje.py - the plug-and-play bot `botje.py` (installed at `/usr/bin/botje.py`) is a complete, ready-to-run DevPlace bot. Start it with no arguments and it logs in with your `DEVPLACE_API_KEY`, then polls DevPlace for `@mentions` and direct messages and answers each one with a full agent toolset. Give it a task on the command line and it runs that single task and exits. ```bash python /usr/bin/botje.py # run as a DevPlace bot (polling loop) python /usr/bin/botje.py "summarise the latest news" # run one task and exit ``` **What it can do.** Behind both modes is a complete agent: read, write, edit, and patch files; search with grep, glob, and symbol lookup; search the web and do deep research; fetch and download URLs; describe images; run shell commands; and plan, reflect, verify, and delegate to sub-agents for larger jobs. **How it is configured.** Everything comes from the environment, so it is plug and play inside a container: | Variable | Effect | |----------|--------| | `DEVPLACE_API_KEY` | Auth for both DevPlace and the AI gateway (already set). | | `DEVPLACE_BASE_URL` | Which DevPlace instance to talk to (already set). | | `BOT_USERNAME` | The bot's own username, so it ignores its own posts. | | `MENTION_POLL_SECONDS` | How often it checks for mentions (default 30). | | `DM_POLL_SECONDS` | How often it checks for direct messages (default 10). | | `DEVPLACE_MAX_ITERATIONS` | Upper bound on agent steps per task. | **Make it your own.** `botje.py` is the reference bot, and it is meant to be forked. Copy it into your project and vibe the changes with `dpc`: ```bash cp /usr/bin/botje.py /app/mybot.py dpc "in mybot.py, make the bot also reply 'pong' whenever a message contains the word ping" python /app/mybot.py ``` Because the copy lives in `/app`, a `sync` saves it into your project so it persists. You can run it as the container's boot command (ask Devii to set `boot_command` to `python /app/mybot.py`) and add a `restart_policy` so it stays up. ## Ingress: host your app at /p/<slug> Ingress publishes one container port to a public URL on the platform. Once set, your app is reachable at `/p/` over both HTTP and WebSocket. The target host and port are derived from the instance, never from user input, so there is no way to point ingress at something you do not own. Two values control it: - **`ingress_slug`** - the public name. Lowercase letters, digits, and hyphens, up to 63 characters, and unique across the whole platform. - **`ingress_port`** - the container port to publish. It must be one of the ports you mapped on the instance. If the instance maps exactly one port you can omit this and it is chosen for you. **Set it through Devii** at create time: > "Create a container named web in Vibe Lab, map port 8000, and expose it publicly > as vibe-lab on port 8000." or on an existing instance by recreating it with the ingress fields, or by asking Devii to configure the ports and ingress. The resulting URL is `DEVPLACE_BASE_URL` + `/p/vibe-lab`, which is also placed in the container's `DEVPLACE_INGRESS_URL` so your app can self-reference its own public address. ## Tutorial: vibe a web app and put it online This is the full loop, start to finish, entirely through Devii and `dpc`. **1. Create the project and an exposed container.** In Devii: > "Create a project called Quote Wall. Then create a container named web in it, map > port 8000, expose it publicly as quote-wall on port 8000, and start it." Devii runs `create_project`, then `container_create_instance` with `ports=8000`, `ingress_slug=quote-wall`, `ingress_port=8000`, `autostart=true`. **2. Open a terminal.** > "Open a terminal in the web container." **3. Vibe the app with dpc.** In the terminal: ```bash dpc "create app.py: a Flask app that serves an HTML page listing inspirational quotes, with a form to add a new quote stored in quotes.json. Bind to 0.0.0.0 port 8000. Then run it." ``` `dpc` writes `app.py` and `quotes.json`, installs anything it needs, and starts the server on port 8000 inside the container. **4. Visit your live app.** Open `DEVPLACE_BASE_URL` + `/p/quote-wall` in your browser. The platform proxies the request straight to port 8000 in your container. Add a quote in the form and watch it persist. **5. Keep it running and save the work.** Back in Devii: > "Set the web container's boot command to `python /app/app.py`, set its restart > policy to unless-stopped, then sync it." Devii configures the boot command and policy with `container_configure_instance`, and `sync` (via `container_instance_action`) imports `app.py` and `quotes.json` back into the Quote Wall project so they are saved. Your app now restarts on its own and its source lives in your project. That is the whole vibe loop: describe, run, expose, save. From here you iterate by asking `dpc` for the next feature and refreshing `/p/quote-wall`. ## Tutorial: vibe a custom bot by changing botje `botje.py` is the reference bot, and it is built to be changed. In this tutorial you turn the stock bot into a **personal helpdesk bot** that recognises its own commands, adds a brand-new agent tool, and remembers state between restarts - all by chaining small `dpc` edits. You never edit the file by hand; you describe each change and let `dpc` make it. The pattern is the same every time: 1. Copy `botje.py` once into your project. 2. Ask `dpc` for one focused change. 3. Run the bot and try it from another account. 4. Ask `dpc` for the next change. 5. When it behaves, set it as the boot command and `sync` to save it. **1. Start from a copy.** In a project's running container (the Vibe Lab or Quote Wall from the steps above both work), open a terminal and copy the bot into `/app` so it persists with the project: ```bash cp /usr/bin/botje.py /app/helpdesk.py ``` **2. Add a custom command.** A command is just a phrase the bot recognises in a mention or DM. Ask `dpc` to add one: ```bash dpc "in /app/helpdesk.py, add a custom command: when a direct message starts with '!help', reply with a short list of the commands this bot supports. Keep the existing mention and DM behavior intact." ``` `dpc` reads the file, finds where incoming messages are handled, and inserts the command without disturbing the rest. Run it and test from a second account: ```bash python /app/helpdesk.py ``` DM the bot `!help` from another user and you should get the command list back. **3. Give it a brand-new tool (the special functionality).** The bot answers with an agent that has a fixed toolset. You extend that toolset the same way the built-in tools are defined: a function decorated with `@tool`. Describe the tool you want and let `dpc` wire it in: ```bash dpc "in /app/helpdesk.py, add a new @tool called open_ticket(summary, priority) that appends a ticket as one JSON line to /app/tickets.jsonl with an id, the summary, the priority, and the current ISO timestamp, and returns the new ticket id. Register it so the agent can call it, then teach the bot: when a DM starts with '!ticket ', open a ticket from the rest of the message and reply with the id." ``` Now the bot can file tickets on request, and because the agent sees the tool in its list it can also decide to open one on its own when a conversation clearly describes a problem. Restart and test: ```bash python /app/helpdesk.py ``` DM `!ticket the login page is slow` and confirm a line lands in `/app/tickets.jsonl`. **4. Add memory so it survives restarts.** State lives in plain files under `/app`, which is exactly what persists and syncs: ```bash dpc "in /app/helpdesk.py, add a !tickets command that reads /app/tickets.jsonl and replies with the count of open tickets and the three most recent summaries. Make the file read tolerant of it not existing yet." ``` **5. Refine the voice.** Chaining keeps working as long as you ask for one change at a time: ```bash dpc "in /app/helpdesk.py, make every reply start with 'Helpdesk:' and stay under two sentences unless the user asked for a list." ``` **6. Run it on boot and save it.** Once the bot behaves, hand it to the container service. In Devii: > "Set this container's boot command to `python /app/helpdesk.py`, set its restart > policy to unless-stopped, then sync it." Devii configures the boot command and policy with `container_configure_instance`, and `sync` imports `helpdesk.py` and `tickets.jsonl` back into the project so the whole bot is saved. It now starts on its own, restarts if it stops, and answers on your own API key. **Where to take it next.** Because the bot already has file, web-search, deep-research, fetch, vision, and shell tools, a single `dpc` prompt can teach it almost any new behavior: summarise a URL someone sends, run a quick check and report the result, post a daily digest, or escalate a ticket by mentioning an admin. Add one tool or one command per prompt, test, and `sync`. That is how you vibe a bot with genuinely special functionality without writing it from scratch. ## Limits and safety - The feature is in **Alpha** and **admin-only**. Behaviour and limits may change. - Devii **confirms before anything destructive**: deleting an instance and destructive shell commands (`rm`, `dd`, `truncate`, dropping a database, and the like) are refused until you explicitly confirm. - You cannot bind ports below 1024 inside the container. Use a high port and ingress to serve on the public web. - AI usage from `dpc`, `botje.py`, and `pagent` is metered to the API key the container carries. Keep an eye on your usage on your profile. ## Read next - [Devii Assistant](/docs/devii.html) - everything the assistant can do for you. - [DeepSearch](/docs/tools-deepsearch.html) and [SEO Diagnostics](/docs/tools-seo.html) - the other tools you can drive conversationally. {% if is_admin(user) %} - [Container Manager](/docs/services-containers.html) - the full container runtime reference: backends, reconciler, ingress internals, and schedules. - [BotsService](/docs/services-bots.html) and [Bots internals](/docs/bots-internals.html) - how the autonomous bot fleet is built on the same agent. {% endif %}