# Build a Conduck adapter — brief for AI coding tools

Brief revision 2026-07-19, written against adapter contract v1, document revision 1.3. Non-normative: where this brief and the contract disagree, the contract wins.

You are an AI coding tool. Your operator wants to connect an AI engine they run — a CLI agent, a local model, a framework of their own — to the Conduck app on their phone, watch, car, or desktop. You will build the small HTTP **adapter** that sits in front of that engine, verify it with Conduck's automated conformance checker (the "doctor"), and hand a working, paired setup back to your operator.

## Definition of done — read this first

The build is done ONLY when the ordinary AND the deep doctor both exit `0` against the REAL engine on loopback:

```bash
curl -fsSLO https://github.com/gigaduckai/conduck-connect/releases/latest/download/conduck-connect.sh
CONDUCK_TOKEN="$TOKEN" bash conduck-connect.sh --doctor http://127.0.0.1:8480
CONDUCK_TOKEN="$TOKEN" bash conduck-connect.sh --doctor --deep http://127.0.0.1:8480
```

- The gate is **exit status 0 under the doctor profile you ran** — never a count of checks (counts change between doctor versions).
- Do not report the build complete until both pass against the real engine. Report the doctor's version, the profile (`--doctor` vs `--deep`), the exit status, and — if your doctor version prints one — its machine-readable `CONDUCK_DOCTOR` summary line.
- Passing the build gate does NOT finish the task: steps 9–12 below are deployment acceptance, done with your operator. A foreground terminal session is not a deployment.

## Workflow

1. **Fetch the contract.** Read `https://conduck.com/setup/adapter/v1.md` — the normative wire contract. (`…/v1.md` is the raw file and `…/setup/adapter/v1/` is the same document rendered as a page — one contract, two URLs; cite either.) Record the document revision you are building against (this brief targets revision 1.3) and, once downloaded, the doctor version you test with.
2. **Inspect the existing engine.** Look at the operator's actual project before writing anything — don't guess. How is a turn invoked? Is it stateless? Can turns run concurrently? Does it accept images? If a long-lived HTTP server implementing the contract's two routes already exists, nothing needs to be built.
3. **Decide the profile with your operator:** listening port (default `8480`), token, exact engine invocation, concurrency policy, image behavior (forward vs decline), and whether to enable the optional file lane (a shared working folder).
4. **Scaffold.** A loopback-only HTTP server with the two contract routes, plus a development-only `--engine echo` mode that deterministically echoes the latest user message. Echo mode exists for conformance testing; never pair it as the production engine.
5. **Doctor loop against echo.** Run the ordinary doctor, then `--deep`, against the echo engine. Fix and re-run until exit `0`. This proves your HTTP surface before the engine complicates the picture.
6. **Integrate the real engine.** Per the contract's security floor: internal deadline with whole-child-process-tree termination, a queue with early `429`, sandboxing appropriate to the engine, no-content logging, and a scrubbed child environment (see the token rule below).
7. **Write your own tests for what the doctor cannot see over HTTP:** turn cancellation actually kills the engine process tree, file-lane confinement holds, temp state is cleaned up, secrets never appear in logs or child environments.
8. **Doctor loop against the real engine** — ordinary and `--deep`, until exit `0`. This is the build gate from "Definition of done".
9. **Install under a supervisor** (launchd, systemd, or equivalent) so the adapter survives restarts and logouts. Conduck assumes an always-on gateway.
10. **Expose and pair.** Run the setup wizard on the adapter's machine: `curl -fsSLO https://github.com/gigaduckai/conduck-connect/releases/latest/download/conduck-connect.sh && bash conduck-connect.sh --generic`. It handles HTTPS exposure and prints the QR pairing code. Honesty note: the wizard needs a real interactive terminal (a PTY) and will re-ask for the port, token, and working folder — hand this step to your operator at a terminal if you cannot drive a PTY, and tell them the values you configured.
11. **Doctor once more against the exposed HTTPS URL,** where a trusted certificate permits it — this catches proxy-added surprises the loopback run can't.
12. **Live acceptance with the operator, from a real device:** a text turn; a photo on the current turn; a text turn in a conversation that contains an earlier photo; and, if the file lane is enabled, one inbound file and one outbound file.

## Environment conventions

These are operational conventions, not wire contract — they make the doctor, the wizard, and community adapters line up.

| Variable | Rule |
|---|---|
| `CONDUCK_TOKEN` | Required. The bearer token the adapter expects. Opaque — compare exactly, don't validate format. When you generate it: at least 32 random bytes (e.g. 64 hex characters). NEVER pass it into the child AI engine's environment, and never log it. |
| `CONDUCK_PORT` | Optional, default `8480`. Loopback only — the adapter binds `127.0.0.1`, never a public interface. |
| `CONDUCK_FILES_DIR` | Optional. Absolute path to an existing directory — exactly the shared working folder of the contract's file lane, and the only place the engine reads inbound / writes outbound files. Unset = file lane off. |

`--engine echo` is the conventional flag for the development echo mode from step 4.

## Deliverables

All of these, in the project:

- **The adapter code.** Prefer the standard library of your language; document any dependency and its exact install command in the README.
- **`README.md`** — one command to start each engine mode, the environment variables above, and the supervisor install.
- **`SELF-TEST.md`** — transcript of your verification: the doctor runs (version, profile, exit status, and the `CONDUCK_DOCTOR` summary line if printed) and your own step-7 tests.
- **`SPEC-FEEDBACK.md`** — every ambiguity, gap, or error you found in the contract while building. Leave it empty only if there are truly none. For each finding: the contract section, what was ambiguous or missing, the choice you made, and a suggested fix. This file is a first-class deliverable — every community build reviews the spec.

### Submitting spec feedback (opt-in)

Feedback on the contract is genuinely wanted — it is how this document improves. The route: a GitHub issue on [`gigaduckai/conduck-connect`](https://github.com/gigaduckai/conduck-connect). But it is your OPERATOR's call, not yours:

1. Draft the issue body from `SPEC-FEEDBACK.md`.
2. Redact ruthlessly: no tokens, no URLs or hostnames, no message content, no filenames or paths from the operator's machine.
3. Show the operator the exact redacted body and ask whether to submit.
4. Submit only with their explicit approval — or hand them the draft if you can't authenticate with GitHub.

## Security requirements

The contract's "Security floor" section is binding — read it in full. Beyond it:

- The child AI engine's environment must be scrubbed: it never receives `CONDUCK_TOKEN`, and it never receives credentials it doesn't need.
- Error messages are static strings — never echo request content, filenames, or header values into responses or logs.

### Hardening checklist

Distilled from a hardened reference build. Each line is a SHOULD — skip one only as a deliberate choice documented with your operator:

- **Engine credential:** load from the environment or a `0600` file — never config plaintext, argv (visible in `ps`), or logs. Then prove the posture: a deliberately invalid key must fail the turn, never silently fall back to another credential source on the machine (keychains and config dirs are readable by same-user processes — scrubbing environment variables alone is not isolation).
- **Replacement environment, not overlay:** build the engine's environment as an explicit allowlist (its credential, a controlled `PATH`, locale, temp dir). Inherit-and-remove leaks whatever you forgot to remove; SSH, git, and cloud credentials must be structurally absent.
- **Least capability:** if the engine has tool or sandbox settings, grant the smallest set the use case needs, deny by default. A read-only assistant needs no shell, no writes, no web.
- **Fail closed:** any policy or validation check that throws must yield a denial, never an allow-through.
- **Own the whole process tree:** spawn the engine in its own process group and kill the group at the deadline — killing only the direct child orphans its tool subprocesses.
- **Per-turn state isolation:** give the engine per-turn `HOME`/config/temp directories under an adapter-owned `0700` dir, deleted on every exit path — success, error, and kill alike.
- **The working folder is one trust domain:** anyone with the token can have the engine read any file in it, and every file read may be sent to the AI provider — keep secrets out, and say this plainly in your README. Canonicalize the folder once at startup; reject paths and symlinks that resolve outside it (canonical containment, not string-prefix comparison).
- **Engine output is untrusted input:** cap sizes, parse strictly, and never surface raw engine stdout/stderr in HTTP responses or logs.
- **One deadline for the whole request:** the clock starts at HTTP arrival and covers body read, queue wait, the engine turn, and cleanup. On expiry: kill, clean up, return an error — never a partial answer as `200`.
- **Diagnostics are codes:** logs — and any `--debug` mode — carry lifecycle events and stable error codes only, never message content, images, tokens, URLs, filenames, or prompts.

## Terms-of-service note

A Conduck adapter connects your operator's own AI — their key, their login, their machine — to their own devices. Do not build it to expose that AI to third parties, share accounts, or resell access; that breaks most AI providers' terms (and Conduck's spirit: everyone brings their own AI).
