<div class="docs-content" data-render>
# 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/<slug>`.
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 <package>` 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.
## 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/&lt;slug&gt;
Ingress publishes one container port to a public URL on the platform. Once set, your
app is reachable at `/p/<slug>` 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 %}
</div>