{
  "status": "proposed",
  "note": "Draft for review. Not a supported Saylek interface. The supported machine interfaces are `saylek capabilities --json` and `saylek --version --json`; each command's JSON envelope is its own. Prefer them where they disagree with this draft.",
  "schema": "saylek-agent-setup-draft/0",
  "verified_against": {
    "stableChannel": "0.1.2650",
    "installedBuild": "0.1.2650-r2650+g198902d92e94",
    "installerDefault": "0.1.2672",
    "cliFlagsUnchangedIn": "0.1.2672",
    "readOn": "2026-10-25"
  },
  "supported_interfaces": [
    {
      "command": "saylek capabilities --json",
      "operation": "capabilities.describe",
      "schema_version": 1
    },
    {
      "command": "saylek --version --json",
      "operation": "client.version",
      "schema_version": 1
    }
  ],
  "authorization": {
    "public_docs_are_authorization": false,
    "unattended_flag_is_consent": false,
    "reuse_bounded_prior_authorization": true,
    "person_enters_credentials": true,
    "setup_changes_sharing": false
  },
  "permissions": [
    {
      "id": "read",
      "label": "Read this machine",
      "covers": "Commands that only read: version, capability list, status, sharing state, help.",
      "rule": "No permission needed."
    },
    {
      "id": "network-read",
      "label": "Read from saylek.com",
      "covers": "Fetching release information or the installer text to read it, without saving or running it.",
      "rule": "No permission needed beyond being allowed to use the network."
    },
    {
      "id": "download",
      "label": "Download",
      "covers": "Saving the installer, a release or model files. Models can need substantial disk space and time.",
      "rule": "Needs approval, naming what is downloaded."
    },
    {
      "id": "install",
      "label": "Install or update",
      "covers": "Installing or replacing the Saylek binary and its background service.",
      "rule": "Needs approval, naming the release."
    },
    {
      "id": "local-change",
      "label": "Change local settings",
      "covers": "Adopting a runtime, choosing GPUs and models, writing a diagnostic file, starting or stopping Saylek.",
      "rule": "Needs approval, naming the change."
    },
    {
      "id": "account",
      "label": "Account",
      "covers": "Signing in and connecting this machine to an account. Signing in by emailed code can create or reactivate an account.",
      "rule": "The person approves sign-in themselves."
    },
    {
      "id": "credential",
      "label": "Credentials",
      "covers": "Codes, application keys and runtime tokens.",
      "rule": "The person enters or approves them where the tool asks. Do not read, print or paste one into chat or a command line. Saylek stores what it needs in its own files."
    },
    {
      "id": "model-request",
      "label": "Model request",
      "covers": "Sending a prompt, from Saylek or from an app.",
      "rule": "Needs approval before the first request."
    },
    {
      "id": "sharing",
      "label": "Sharing",
      "covers": "Turning sharing on or off, pausing a machine, choosing shared models, inviting or accepting people.",
      "rule": "Only on an explicit instruction for that action. Other people may depend on it."
    },
    {
      "id": "destructive",
      "label": "Remove",
      "covers": "Uninstalling, removing a runtime, signing out, removing a machine, replacing keys.",
      "rule": "Only on an explicit instruction. Show the plan first; the person supplies any confirmation word."
    }
  ],
  "branches": [
    {
      "id": "app-only",
      "label": "App only",
      "guide": "/docs/first-answer-in-an-app",
      "when": "The person wants an app to use models their account can already reach. No Saylek installation on this machine."
    },
    {
      "id": "existing",
      "label": "Already installed",
      "when": "A Saylek binary is found. Keep its release lane and its settings; update only if asked."
    },
    {
      "id": "new",
      "label": "New installation",
      "when": "No Saylek binary is found and the person wants this machine connected."
    }
  ],
  "phases": [
    {
      "id": "discover",
      "title": "Discover what is here",
      "steps": [
        {
          "id": "find-binary",
          "title": "Look for a Saylek binary",
          "command": "command -v saylek || ls -l \"$HOME/.saylek/bin/saylek\"",
          "permissions": [
            "read"
          ],
          "branches": [
            "existing",
            "new"
          ],
          "apply_if": "The person wants Saylek on this machine. Skip for app only.",
          "does": "Prints where `saylek` is. The installer puts it in `~/.saylek/bin`, which a new shell may not have on its PATH yet. If neither prints a path, no binary was found in these places.",
          "evidence": "Served install.sh: installs to `$HOME/.saylek/bin` and adds it to PATH in the shell profile."
        },
        {
          "id": "installed-version",
          "title": "Read the installed version",
          "command": "saylek --version --json",
          "permissions": [
            "read"
          ],
          "branches": [
            "existing"
          ],
          "apply_if": "A binary was found. Use its full path if `saylek` is not on PATH.",
          "does": "Prints a `client.version` envelope with the release and build commit.",
          "stop_if": "It fails or prints no JSON: the build may predate this interface, the path may be wrong, or it may not run here. Run `saylek --version` for the plain version, report it, and do not assume any command in this guide works on that build.",
          "evidence": "0.1.2650 capabilities: global operation `saylek --version --json`, read, envelope `client.version`."
        },
        {
          "id": "capabilities",
          "title": "Read what this build can do",
          "command": "saylek capabilities --json",
          "permissions": [
            "read"
          ],
          "branches": [
            "existing"
          ],
          "apply_if": "A binary was found and printed its version.",
          "does": "Lists each command with its effect (read, mutate, destructive), whether it needs a person, prerequisites, any confirmation word and whether it has structured output. Read its warnings too. Works offline. Each command's JSON shape is its own; many commands have none yet.",
          "stop_if": "The command is missing, or a command this plan needs is missing or unclassified. Do not guess: hand back with what you found.",
          "evidence": "0.1.2650 capabilities: read, unattended, envelope `capabilities.describe`; 114 commands, 30 with an envelope, 79 unmigrated, 5 unsupported."
        },
        {
          "id": "status",
          "title": "Check whether Saylek is running",
          "command": "saylek status --json",
          "permissions": [
            "read"
          ],
          "branches": [
            "existing"
          ],
          "apply_if": "A binary was found.",
          "does": "Exit code 0 means up, 1 down, 2 degraded, 3 misconfigured. Read the output as well as the code.",
          "evidence": "0.1.2650 `status --help`; capabilities: read, envelope `client.status`."
        },
        {
          "id": "sharing-state",
          "title": "Read the sharing state and the shared-model selection",
          "command": "saylek host status --json && saylek host models --json",
          "permissions": [
            "read"
          ],
          "branches": [
            "existing"
          ],
          "apply_if": "A binary was found.",
          "does": "`host status` shows whether this machine is sharing. `host models` with no ids shows what it offers now. With no selection named, a sharing machine offers every model it serves. Report both before changing anything.",
          "evidence": "0.1.2650 `host status --help` (read, envelope `machines.status`); `host models --help`: no ids shows the offer, naming ids or `--clear` changes it (the capability row is mutate for the command as a whole)."
        },
        {
          "id": "update-check",
          "title": "Compare with the published release",
          "command": "saylek update --check",
          "permissions": [
            "network-read"
          ],
          "branches": [
            "existing"
          ],
          "apply_if": "A binary was found.",
          "does": "Compares this build with the stable release published by the place this machine was installed from. Downloads and installs nothing. The answer can be current, behind, ahead (a newer or development build) or not comparable; read the words, an exit code alone does not say which.",
          "stop_if": "It cannot reach the release information, or says the versions cannot be compared. Report what it printed.",
          "evidence": "v0.1.2650 `cli/update.rs`: reads the install origin, compares base versions, reports ahead and unorderable pairs verbatim; `--check` touches nothing."
        },
        {
          "id": "platform",
          "title": "Check the platform",
          "command": "uname -s -m",
          "permissions": [
            "read"
          ],
          "branches": [
            "new"
          ],
          "apply_if": "No binary was found.",
          "does": "The installer supports macOS on Apple silicon (Darwin arm64) and Linux on x86_64 with the GNU C library.",
          "stop_if": "Any other platform. The installer refuses it; say so and point to the hardware guide.",
          "evidence": "Served install.sh platform detection (Linux/x86_64, Darwin/arm64; musl refused)."
        },
        {
          "id": "release-info",
          "title": "Read which release the installer would install",
          "command": "curl -fsSL https://saylek.com/m/stable && curl -fsSL https://saylek.com/install.sh | grep -E '^(VERSION|STABLE_VERSION)='",
          "permissions": [
            "network-read"
          ],
          "branches": [
            "new"
          ],
          "apply_if": "No binary was found.",
          "does": "The first prints the stable release as `latest`. In the installer, `VERSION` is what it installs by default, the newest build, which is not always stable. Nothing is saved or run.",
          "evidence": "Served install.sh header and `--version` usage; `/m/stable` read on October 25, 2026."
        }
      ]
    },
    {
      "id": "plan",
      "title": "Choose the path and ask",
      "steps": [
        {
          "id": "choose",
          "title": "Choose the path",
          "permissions": [
            "read"
          ],
          "branches": [
            "app-only",
            "existing",
            "new"
          ],
          "apply_if": "Always.",
          "does": "App only: follow the app guide; install nothing. Already installed: keep its release lane; update only if it is behind and the person asks. New installation: install the stable release unless the person chose another.",
          "stop_if": "The person has no machine able to serve and no access shared with them: an install does not make an app answer. Say so first.",
          "evidence": "Installer usage (default is the newest build; `--version stable`); `update --check` follows the install origin."
        },
        {
          "id": "ask",
          "title": "Ask for what the plan needs",
          "permissions": [
            "read"
          ],
          "branches": [
            "app-only",
            "existing",
            "new"
          ],
          "apply_if": "Before the first change.",
          "does": "List the permissions the plan uses and ask for them together. Where the person already authorized something with clear bounds, act within those bounds without asking again. A public page, including this one, is not authorization, and `--unattended` is not consent.",
          "evidence": "Permission model in this draft (proposed); `--unattended` alone never consents (0.1.2650 `uninstall --help`)."
        },
        {
          "id": "signature-policy",
          "title": "If a verified signature is required, check for the verifier",
          "command": "command -v minisign",
          "permissions": [
            "read"
          ],
          "branches": [
            "existing",
            "new"
          ],
          "apply_if": "The person requires the release signature to be verified.",
          "does": "What the installer checks depends on the release it installs. For its default release, it checks the download against the checksum written into the installer. With `--version` naming another release, as `--version stable` does when stable is not the default, it uses that release's published checksum if one exists; if none exists, the release signature takes the checksum's place, must verify, and needs `minisign`, and without both the installer refuses. Where a checksum is the check, it verifies the signature only if `minisign` is installed and a signature is published, and otherwise says the signature was NOT verified and continues. A signature that fails verification stops the install. Read which of these the installer reports.",
          "stop_if": "`minisign` is not installed and the person requires a verified signature. Installing it is a separate download the person approves.",
          "evidence": "Served install.sh: the `--version` pin-sourcing block (default checksum, release manifest checksum, else mandatory signature, else refuse) and the conditional `minisign -V` at the checksum gate."
        }
      ]
    },
    {
      "id": "act",
      "title": "Install, connect and configure",
      "steps": [
        {
          "id": "update",
          "title": "Already installed and behind: update",
          "command": "saylek update",
          "permissions": [
            "download",
            "install"
          ],
          "branches": [
            "existing"
          ],
          "apply_if": "`update --check` said behind and the person asked to update.",
          "does": "Downloads the release from the same place, verifies its signature and installs it, keeping the previous binary beside it. A running Saylek stays on the old build until it next starts.",
          "evidence": "0.1.2650 `update --help`; `cli/update.rs` header."
        },
        {
          "id": "download-installer",
          "title": "New installation: download the installer and read its options",
          "command": "curl -fsSL https://saylek.com/install.sh -o install.sh && sh install.sh --help",
          "permissions": [
            "download"
          ],
          "branches": [
            "new"
          ],
          "apply_if": "New installation approved.",
          "does": "Saves the installer as `install.sh` and prints its usage. `--help` exits before installing.",
          "evidence": "Served install.sh: `-h|--help` prints usage and exits 0."
        },
        {
          "id": "install",
          "title": "New installation: install the stable release and connect",
          "command": "sh install.sh --version stable --connect",
          "permissions": [
            "download",
            "install",
            "account"
          ],
          "conditional_permissions": [
            {
              "permission": "local-change",
              "when": "The installer finds a running runtime and offers to adopt it."
            }
          ],
          "branches": [
            "new"
          ],
          "apply_if": "New installation, connecting this machine, approved.",
          "does": "Downloads and installs the stable release with its background service, then the person approves this machine in a browser and it is recorded on their account. Read and record what the installer says it checked: a checksum, a signature, or both, and whether the signature was verified. If it offers to adopt a runtime it found, that is a local change the person approves. Turning sharing on is a separate step this command does not take.",
          "stop_if": "Sign-in is not approved, a step fails, or the signature outcome does not meet the person's policy. Report the installer's own message; do not retry with other flags.",
          "evidence": "Served install.sh usage (`--version`, `--connect`), its connect finale and its integrity block."
        },
        {
          "id": "recheck",
          "title": "After installing or updating: read capabilities and sharing again",
          "command": "saylek capabilities --json && saylek host status --json && saylek host models --json",
          "permissions": [
            "read"
          ],
          "branches": [
            "existing",
            "new"
          ],
          "apply_if": "After a new installation or an update, before adopting a runtime or any other change.",
          "does": "The build may differ from the one read earlier, and a connected or reconnected machine may already have sharing settings. Reread what this build can do, whether the machine is sharing and which models it offers.",
          "stop_if": "A command the plan needs is missing or unclassified, or sharing is on in a way the person did not expect. Report it before going on.",
          "evidence": "0.1.2650 capabilities (`capabilities.describe`, `machines.status`) and `host models --help` (no ids shows the offer)."
        },
        {
          "id": "probe-runtime",
          "title": "Existing runtime: check it without adopting it",
          "command": "saylek host engine probe http://127.0.0.1:11434",
          "permissions": [
            "read"
          ],
          "branches": [
            "existing",
            "new"
          ],
          "apply_if": "The machine already runs a model runtime.",
          "does": "Reports what runs at that address and its models. Saves nothing. Use the runtime's actual address; 11434 is Ollama's default.",
          "evidence": "0.1.2650 `host engine probe --help`; capabilities: read, envelope `machines.engines.probe`."
        },
        {
          "id": "adopt-runtime",
          "title": "Existing runtime: adopt it",
          "command": "saylek host engine add http://127.0.0.1:11434 --no-advertise",
          "permissions": [
            "local-change"
          ],
          "conditional_permissions": [
            {
              "permission": "credential",
              "when": "The runtime needs a token, passed with `--token-env`."
            }
          ],
          "branches": [
            "existing",
            "new"
          ],
          "apply_if": "The probe found the runtime, the sharing state and selection were read after any install or update, and the person approved.",
          "does": "Adds the runtime as a source for this machine. `--no-advertise` does not add its models to the shared-model selection and does not remove any already in it: a model id already selected stays offered once this runtime serves it. If no selection is set and sharing is on, this machine offers every model it serves, this runtime's included. Before adopting, compare the probe's model ids with the selection read after setup, and confirm what the person intends to share. A runtime that needs a token takes `--token-env` with the NAME of a variable the person set; Saylek keeps a private copy of the token so serving works after a reboot. That makes it a credential step too.",
          "stop_if": "Sharing is on, the runtime's models would be offered (no selection set, or a matching id already selected), and the person has not said they may be. Or the runtime is on another machine, which needs `--allow-remote` and widens network exposure.",
          "evidence": "0.1.2650 `host engine add --help` (`--no-advertise`, `--token-env`, private copy) and `host models --help` (an unset list means no filter); capabilities: mutate, envelope `machines.engines.add`."
        },
        {
          "id": "no-runtime",
          "title": "No runtime yet: run the guided setup",
          "command": "saylek gpu wizard",
          "permissions": [
            "download",
            "local-change"
          ],
          "branches": [
            "existing",
            "new"
          ],
          "apply_if": "No runtime runs and the machine meets the managed-runtime requirements.",
          "does": "Detects GPUs, sets up a runtime and a model, and can download large model files. Interactive by default; the person answers its questions.",
          "stop_if": "The machine does not meet the managed-runtime requirements.",
          "evidence": "0.1.2650 `gpu wizard --help`; capabilities: mutate, unattended with flags."
        }
      ]
    },
    {
      "id": "verify",
      "title": "Check each result",
      "steps": [
        {
          "id": "verify-version",
          "title": "The installed build is the one planned",
          "command": "saylek --version --json",
          "permissions": [
            "read"
          ],
          "branches": [
            "existing",
            "new"
          ],
          "apply_if": "After installing or updating.",
          "does": "Compare the release and build with what the plan named. A new shell may be needed for PATH.",
          "checkpoint": "Version confirmed",
          "evidence": "0.1.2650 capabilities: `saylek --version --json`."
        },
        {
          "id": "verify-status",
          "title": "Saylek is running",
          "command": "saylek status --json",
          "permissions": [
            "read"
          ],
          "branches": [
            "existing",
            "new"
          ],
          "apply_if": "After setup.",
          "does": "Expect exit code 0 and an up status.",
          "stop_if": "Exit code 1, 2 or 3: go to recovery.",
          "checkpoint": "Running",
          "evidence": "0.1.2650 `status --help` exit codes."
        },
        {
          "id": "verify-models",
          "title": "This machine lists a model",
          "command": "saylek models --json",
          "permissions": [
            "read"
          ],
          "branches": [
            "existing",
            "new"
          ],
          "apply_if": "Saylek is running.",
          "does": "Lists the models the running daemon serves. A listed model is not yet an answer.",
          "checkpoint": "Model listed",
          "evidence": "0.1.2650 `models --help`; capabilities: read, prerequisite daemon-running, envelope `models.list`."
        },
        {
          "id": "verify-sharing",
          "title": "Sharing is as the person left it",
          "command": "saylek host status --json && saylek host models --json",
          "permissions": [
            "read"
          ],
          "branches": [
            "existing",
            "new"
          ],
          "apply_if": "After setup.",
          "does": "Compare with what you read before. Setup should not have changed sharing or the selection; if it did, report it.",
          "checkpoint": "Sharing state reported",
          "evidence": "0.1.2650 `host status --help`, `host models --help`."
        },
        {
          "id": "verify-local-answer",
          "title": "This machine answers one prompt",
          "command": "saylek call \"Explain what a GPU does in one sentence.\"",
          "permissions": [
            "model-request"
          ],
          "branches": [
            "existing",
            "new"
          ],
          "apply_if": "A model is listed and the person approved a request.",
          "does": "Sends one prompt to the first model this machine serves and prints the reply and a receipt path. This shows the machine answers; it does not show that an app does.",
          "checkpoint": "Local answer",
          "evidence": "0.1.2650 `call --help`; capabilities: mutate, prerequisite daemon-running."
        },
        {
          "id": "app-config",
          "title": "Get the settings for the app",
          "command": "saylek connect",
          "permissions": [
            "local-change"
          ],
          "branches": [
            "existing",
            "new"
          ],
          "apply_if": "The person wants an app on this machine to use it.",
          "does": "Prints the local address and paste-in settings for common coding agents. Printing settings is not an answer. Changing the app's own settings is a separate change the person approves.",
          "evidence": "0.1.2650 `connect --help`; capabilities classify it as mutate, so this draft does too."
        },
        {
          "id": "verify-app-answer",
          "title": "The chosen app gets an answer",
          "permissions": [
            "local-change",
            "model-request"
          ],
          "branches": [
            "app-only",
            "existing",
            "new"
          ],
          "apply_if": "The app's settings were entered with the person's approval.",
          "does": "Send one short prompt from the chosen app and see the reply inside it. This is the result the person asked for. A reply through Saylek's hosted address does not say which machine served it.",
          "stop_if": "No reply, or an error: use the app guide's recovery table.",
          "checkpoint": "App answer",
          "evidence": "Draft app first-success guide; no Saylek command observes another app's reply."
        }
      ]
    },
    {
      "id": "recover",
      "title": "Recover or stop",
      "steps": [
        {
          "id": "diagnose",
          "title": "Collect a diagnostic file",
          "command": "saylek diagnose",
          "permissions": [
            "local-change"
          ],
          "branches": [
            "existing",
            "new"
          ],
          "apply_if": "Something failed and the person wants support.",
          "does": "Writes a redacted diagnostic file on this machine. Sending it to anyone is a separate decision the person makes.",
          "evidence": "0.1.2650 capabilities: `diagnose`, mutate, envelope `support.bundle`. There is no `doctor` command in this release."
        },
        {
          "id": "stop",
          "title": "Stop Saylek on this machine",
          "command": "saylek stop",
          "permissions": [
            "local-change",
            "sharing"
          ],
          "branches": [
            "existing",
            "new"
          ],
          "apply_if": "The person asks to stop Saylek here.",
          "does": "Stops everything Saylek does here: the person's own local use and any sharing from this machine. Nothing on disk changes; `saylek start` brings it back and keeps the terminal busy while it runs.",
          "evidence": "0.1.2650 `stop --help`, `start --help`."
        },
        {
          "id": "sharing-off",
          "title": "Turn sharing off on this machine",
          "command": "saylek host stop",
          "permissions": [
            "sharing"
          ],
          "branches": [
            "existing",
            "new"
          ],
          "apply_if": "The person asks to stop sharing this machine.",
          "does": "Works offline, on this machine only. The people the person shares with stop being offered its models; the person's own use carries on, and `saylek host start` turns sharing back on. Different from `saylek host pause`, which goes through the account, can reach another machine and also stops requests sent through Saylek from the person's own keys.",
          "evidence": "0.1.2650 `host stop --help`, `host pause --help`."
        },
        {
          "id": "uninstall-plan",
          "title": "See what uninstalling would remove",
          "command": "saylek uninstall --plan",
          "permissions": [
            "read"
          ],
          "branches": [
            "existing",
            "new"
          ],
          "apply_if": "The person asks about removing Saylek.",
          "does": "Describes what would be removed and changes nothing. Uninstalling keeps config, receipts, keys and models, and needs the person's own `--confirm UNINSTALL`.",
          "evidence": "0.1.2650 `uninstall --help`; capabilities: destructive, confirmation word UNINSTALL."
        }
      ]
    }
  ],
  "unknown_state": [
    "No binary found: say where you looked. It is not proof Saylek was never installed.",
    "A version or capability command fails, prints no JSON, or lacks a command you need: report what it printed and stop that path.",
    "A command is unclassified or has warnings in `saylek capabilities --json`: treat it as unknown and do not run it.",
    "`update --check` is offline, ahead or not comparable: report it; do not install to resolve it.",
    "A prerequisite is unmet (not signed in, daemon not running): say which. Fix it only if the person's authorization already covers that fix; otherwise ask.",
    "A step finished partly: report what changed and what did not before doing anything else.",
    "The machine is already connected or sharing: keep that state and report it."
  ],
  "not_during_setup": [
    {
      "command": "saylek host start",
      "why": "turns sharing on"
    },
    {
      "command": "saylek host wizard",
      "why": "sets up sharing"
    },
    {
      "command": "saylek host",
      "why": "answers requests from people the person shares with"
    },
    {
      "command": "saylek host models <id> / --clear",
      "why": "changes which models are shared"
    },
    {
      "command": "saylek host engine add … --advertise",
      "why": "adds a runtime's models to the shared selection"
    },
    {
      "command": "saylek host pause / resume",
      "why": "pauses or resumes a machine through the account"
    },
    {
      "command": "saylek sharing invite / accept",
      "why": "grants or takes access between people"
    },
    {
      "command": "saylek host rotate / reset-key / revoke",
      "why": "replaces or revokes this machine's key"
    },
    {
      "command": "saylek update",
      "why": "installs a release; `--check` only reads"
    },
    {
      "command": "sh install.sh --force-reinstall",
      "why": "replaces an existing installation"
    },
    {
      "command": "saylek start --force",
      "why": "takes the address from another running Saylek"
    },
    {
      "command": "saylek uninstall / wipe / logout",
      "why": "removes software, data or sign-in"
    }
  ],
  "final_report": [
    "The path taken, the binary path, and the release and build before and after.",
    "Each change made and the permission that covered it.",
    "The installer's checksum and signature outcomes, if it ran.",
    "Sharing state and shared-model selection, before and after.",
    "Which checkpoints passed (version, running, model listed, local answer, app answer) and which were not checked.",
    "How to undo each change."
  ]
}
