docs(a2a): A2A server guide, architecture, Telegram and Docker notes
Add docs/a2a.md (concepts, file formats, endpoints, sessions, task lifecycle, configuration, security notes, curl examples) describing what src/Volcanic.AgenticCore.A2A.Server actually does. Add an A2A project paragraph to architecture.md, an "A2A" section to telegram-bot.md (/a2a, notify_owner, isolated sessions, port 8080), A2A_ENABLED/A2A_PUBLIC_URL and the aspnet image to docker.md, and project-map/test-command updates to CLAUDE.md. Mark the A2A server design spec implemented and record the deviations found while building it (A2AHostOptions, synchronous IA2ACallerDirectory.Find, generic WorkspaceCallerDirectory, per-status push payload, split file-part error handling, the outbound guarded handler, the per-caller server/router architecture, the restart grace period, the owner= log field, the aspnet image, and the actual push retry schedule). Note the aspnet image in the docker-deployment spec too, without rewriting its historical decisions.
This commit is contained in:
@@ -15,7 +15,9 @@ docker compose logs -f bot
|
||||
|
||||
## What's in the image
|
||||
|
||||
- The .NET 10 runtime and the published host in `/app`. No .NET SDK, so the agent can't build .NET projects.
|
||||
- The ASP.NET Core 10 runtime (`mcr.microsoft.com/dotnet/aspnet:10.0-noble`) and the published host in `/app`. No
|
||||
.NET SDK, so the agent can't build .NET projects. The image needs the ASP.NET Core runtime, not just the plain
|
||||
.NET runtime, because the [A2A server](a2a.md) hosts Kestrel when it's turned on.
|
||||
- Tools for the agent's `bash`: `bash`, `git`, `ripgrep`, `python3` (with `python-is-python3` and `python3-venv`),
|
||||
`curl`, `ca-certificates`, `tzdata`, `jq`, `less`, `procps`, `openssh-client`.
|
||||
- Node.js 24 from NodeSource, with `npm` and `npx`.
|
||||
@@ -78,6 +80,8 @@ The compose file maps them into the container.
|
||||
| `TRIGGER_MODEL` | `typesafe/jev-1.13` | The Jev model triggers use | Yes |
|
||||
| `GOOGLE_CLIENT_ID` | — | With the secret, turns on Google Calendar ([google-calendar.md](google-calendar.md)) | Yes |
|
||||
| `GOOGLE_CLIENT_SECRET` | — | See above | No, and redacted |
|
||||
| `A2A_ENABLED` | `false` | Serves other agents over A2A on port 8080 ([a2a.md](a2a.md), [telegram-bot.md#a2a](telegram-bot.md#a2a)); passed as `A2A__Enabled`. Needs a domain in Coolify (see below) | No |
|
||||
| `A2A_PUBLIC_URL` | — | External base URL for the agent card and push webhooks, e.g. `https://bot.example.com`; passed as `A2A__PublicUrl`. Required for push notifications to work | No |
|
||||
| `DEFAULT_TIMEZONE` | the container's `TZ` | IANA zone for chats that never ran `/timezone`, e.g. `Europe/Berlin`. An unknown name stops startup | Yes |
|
||||
| `PROCESS_ENV_ALLOW` | — | Comma-separated names or globs let through despite the deny rules, e.g. `GH_TOKEN` | Yes |
|
||||
| `PLAYWRIGHT_ENABLED` | `true` | Browser tools on or off. Set it to `false` for an image built with `INSTALL_PLAYWRIGHT=false` | Yes |
|
||||
@@ -138,7 +142,7 @@ The compose file binds them to directories under `/media/disk1/containers/volcan
|
||||
|
||||
| Volume | Path | Contents |
|
||||
|---|---|---|
|
||||
| `workspaces` | `/data/workspaces` | One directory per chat (`/data/workspaces/<chatId>`): `AGENTS.md` or `CLAUDE.md`, `PERSONA.md`, `MEMORY.md`, `inbox/` (received files), `browser/` (screenshots, downloads), project-level `.volcanic/extensions/`, the Google Calendar token (`.volcanic/google-calendar/token.json`), scheduled jobs (`.volcanic/scheduler/jobs.json`) |
|
||||
| `workspaces` | `/data/workspaces` | One directory per chat (`/data/workspaces/<chatId>`): `AGENTS.md` or `CLAUDE.md`, `PERSONA.md`, `MEMORY.md`, `inbox/` (received files), `browser/` (screenshots, downloads), project-level `.volcanic/extensions/`, the Google Calendar token (`.volcanic/google-calendar/token.json`), scheduled jobs (`.volcanic/scheduler/jobs.json`), A2A callers and tasks (`.volcanic/a2a/`, [a2a.md](a2a.md#files)) |
|
||||
| `agent` | `/data/agent` | Sessions (`sessions/telegram-<chatId>/`), `settings.json`, `extensions/*.dll`, chat time zones (`telegram/chat-settings.json`); a `scheduler/jobs.json` from before jobs were per chat is moved into the workspaces on start |
|
||||
| `claude` | `/data/claude` | The Claude login (credentials and onboarding state) |
|
||||
|
||||
@@ -154,14 +158,24 @@ stored: everything shows as root with mode 777, `chown` is a silent no-op, and `
|
||||
|
||||
1. Create a resource from this repository with the *Docker Compose* build pack.
|
||||
2. Set `TELEGRAM_BOT_TOKEN` and `TELEGRAM_ALLOWED_USER_IDS` (owner first) and any optional variables from
|
||||
[Configuration](#configuration). Leave the domain empty.
|
||||
[Configuration](#configuration). Leave the domain empty, unless you're turning A2A on (next).
|
||||
3. Deploy. The log shows lines such as `Using Claude Code at /usr/bin/claude, model claude-code/claude-opus-5-5` and
|
||||
`Allowed users: <ids>. Workspaces under /data/workspaces`, and the owner gets `🟢 Online.`.
|
||||
|
||||
There is no healthcheck, because the bot serves no HTTP. Lost connectivity is retried with backoff inside the
|
||||
process. A rejected token (`401`) exits with code 1 and invalid configuration exits with code 2;
|
||||
There is no healthcheck, because by default the bot serves no HTTP. Lost connectivity is retried with backoff
|
||||
inside the process. A rejected token (`401`) exits with code 1 and invalid configuration exits with code 2;
|
||||
`restart: unless-stopped` turns either into a restart loop you can see in the logs.
|
||||
|
||||
### A2A
|
||||
|
||||
With `A2A_ENABLED=true` the bot also listens on port 8080 for [A2A](a2a.md) requests. The compose file already
|
||||
`expose`s it to the Coolify network; give the resource a **domain** so Coolify's proxy routes it there (without a
|
||||
domain, nothing outside the container can reach the port). Set `A2A_PUBLIC_URL` to that domain
|
||||
(`https://bot.example.com`) so the agent card and push webhooks advertise the right address — without it they fall
|
||||
back to the inbound request's own host, which is wrong behind a reverse proxy. The log gains
|
||||
`A2A: listening on ...`; if port 8080 is somehow already taken, the bot logs a critical line and exits with code 3
|
||||
instead of starting half-working. Leaving `A2A_ENABLED` unset or `false` opens no port at all, same as before.
|
||||
|
||||
## First run and Claude login
|
||||
|
||||
1. The owner opens the bot, sends `/start`, then `/claude-code-login`.
|
||||
|
||||
Reference in New Issue
Block a user