diff --git a/docs.json b/docs.json index b16cd3f..36a7384 100644 --- a/docs.json +++ b/docs.json @@ -42,6 +42,8 @@ "group": "Building Apps", "pages": [ "sdk/building-apps", + "sdk/deployment", + "sdk/accounts", "sdk/focus-app", "sdk/ambient-app", "sdk/install-steps" diff --git a/sdk/accounts.mdx b/sdk/accounts.mdx new file mode 100644 index 0000000..3ac5e34 --- /dev/null +++ b/sdk/accounts.mdx @@ -0,0 +1,185 @@ +--- +title: Accounts and authentication +description: "Build apps with separate accounts, connection-scoped credentials, OAuth, and remote MCP connectors" +--- + +An installed app has one bundle identity and one or more account connections. +Shared code is installed once. Credentials, account identity, and permission +choices belong to each connection. + +## Choose the authentication flow + +| Flow | Installer setup | CLI interaction | +| --- | --- | --- | +| Public service | Files/dependencies and optional welcome step | Continue when prompted | +| API key, token, or configuration form | `text` step with `scope: connection` | Enter fields; password fields are hidden | +| Browser OAuth | Connection-scoped `oauth` step | Open the authorization URL and complete the callback/code prompt | +| Remote MCP OAuth | Native connector plus connection-scoped OAuth | Same browser flow; firmware handles discovery, registration, and token storage | +| Device-code approval | Provider-specific native setup plan | Open the approval link; CLI waits for completion | +| Browser/VNC setup | Provider-specific native setup plan | Open the device setup URL and complete the displayed steps | + +The CLI drives the setup plan supplied by firmware. Provider endpoints, client +registration, scopes, and callback URLs must match the provider's requirements. +Supporting a setup card does not establish that every provider works with it. + +## Separate installation from account setup + +For a process-based app, use `truffile_version: 4` and `connections: multi` when +multiple accounts are supported. Install-scoped steps prepare shared code; +connection-scoped steps collect or validate credentials for the first account, +additional accounts, and reconnections. + +```yaml +truffile_version: 4 +connections: multi +# Include your metadata and foreground/background process configuration. +steps: + - name: Copy code + type: files + scope: install + files: + - source: service.py + destination: ./service.py + - name: Connect account + type: text + scope: connection + fields: + - name: api_key + label: API key + type: password + env: SERVICE_API_KEY +``` + +This is a setup fragment, not a complete app. Add a provider validation step +before reporting the account as connected. The selected connection's runtime +receives its own `SERVICE_API_KEY`; do not embed secrets in the manifest or +shared app files. For custom Python apps, expose provider-confirmed identity +through `truffle://user-info` so account labels are meaningful. + +## A remote MCP connector with OAuth + +A native connector uses `truffile_version: 5` and +`metadata.foreground.connector`. Firmware connects to the upstream MCP server; +you do not need to write a Python proxy for this example. + +Create a directory containing `icon.png` and this `truffile.yaml`. Use your own +unique bundle ID if you adapt the example for distribution. + +```yaml +truffile_version: 5 +connections: multi +metadata: + name: Todoist Tutorial + bundle_id: org.example.todoist.tutorial + description: Connect Todoist accounts through its remote MCP service. + icon_file: icon.png + foreground: + connector: + upstream_url: https://ai.todoist.net/mcp + transport: + request_timeout_seconds: 45 + max_read_retries: 1 + retry_delay_seconds: 1 + auth: + mode: oauth + app_var_key: todoist_oauth_state + token_endpoint: https://todoist.com/oauth/access_token + resource: https://ai.todoist.net/mcp + overlay: {} + identity: + sources: + - tool: user-info + map: + provider_subject: userId + email: email + display_name: fullName +steps: +- name: Sign in to Todoist + type: oauth + scope: connection + provider: remote_mcp + description: Authorize Todoist, then return to the terminal. + redirect_uri: http://localhost:18791/callback + scopes: + - data:read_write + dynamic_client_registration: true + resource_metadata_endpoint: https://ai.todoist.net/.well-known/oauth-protected-resource/mcp + authorization_server_metadata_endpoint: https://todoist.com/.well-known/oauth-authorization-server + registration_endpoint: https://todoist.com/oauth/register + oauth_resource: https://ai.todoist.net/mcp + client_name: Todoist Tutorial + auth_endpoint: https://todoist.com/oauth/authorize + token_endpoint: https://todoist.com/oauth/access_token + token_endpoint_auth_method: none + client_id_env: TODOIST_CLIENT_ID + client_secret_env: null + app_var_key: todoist_oauth_state + prompt: null + access_type: null + include_granted_scopes: false +alias_from: +- identity.email +- identity.display_name +``` + +This follows the Todoist flow verified with two separate accounts. The +connector and OAuth step use the same `app_var_key`, which identifies +connection-scoped token state. The `identity` mapping reads Todoist's +`user-info` tool; other providers need their own verified identity mapping. + +```bash +truffile validate ./todoist-tutorial +truffile deploy ./todoist-tutorial +truffile connections add "Todoist Tutorial" +truffile connections list "Todoist Tutorial" --json +truffile connections permissions "Todoist Tutorial" read-only +``` + +The permissions command prompts for the account. Repeat it for each account +you want to restrict. The provider's OAuth scopes and Truffle's account +permissions are separate: granting a provider scope does not override Truffle's +permission settings. + +## OAuth callbacks and credentials + +If the browser runs on the same computer as the CLI, the local callback +listener can receive a registered localhost redirect. If you open the browser +on another computer, copy its final localhost callback URL into the waiting +CLI prompt, even if that browser cannot load the callback page. The CLI +validates the state against the pending request. + +Use the current sign-in link. Firmware may expire a setup after 15 minutes; +a longer CLI timeout does not extend that window. Start a fresh setup after +expiry. Never reuse a previous callback for another account. + +Firmware exchanges and stores native OAuth credentials. Custom Python apps +should use the matching runtime credential helpers; connection-scoped tokens +must not be copied into app-global variables or source files. Native connectors +handle credential use and refresh through firmware. + +## Account lifecycle and permissions + +```bash +truffile connections reconnect "Todoist Tutorial" +truffile connections rename "Todoist Tutorial" Work +truffile connections permissions "Todoist Tutorial" Work auto-review --notes "Review external writes" +truffile connections delete "Todoist Tutorial" Work +truffile deploy ./todoist-tutorial --update +``` + +Friendly app names, bundle IDs, and app UUIDs work. An omitted account opens a +picker. In the rename example, choose an account and name it `Work`. To avoid +prompting, supply an exact account alias or connection UUID; deletion also +requires `--yes` in scripts. + +New accounts use device policy unless an account override exists. Set an +override with `read-only`, `read-write`, or `auto-review`; `default` removes the +override and inherits device policy. The CLI does not change device-wide +defaults or offer an atomic install-and-set-permissions option. Tool annotations +such as `readOnlyHint` describe tool behavior; they are not permission grants. + +An uploaded `--update` preserves installed accounts and their settings. +`--replace` intentionally resets accounts after confirmation. Test both accounts +after an update and verify that reconnecting one leaves the other intact. +See [deployment and account commands](/sdk/deployment) for cancellation, +noninteractive forms, packaging rules, and firmware requirements. diff --git a/sdk/building-apps.mdx b/sdk/building-apps.mdx index 39b0cd2..d6388ab 100644 --- a/sdk/building-apps.mdx +++ b/sdk/building-apps.mdx @@ -11,10 +11,15 @@ A Truffle app is a containerized Python program that runs on your Truffle and ex | **Background** | On a schedule | Submits context to the proactive agent | | **Hybrid** | On demand + on a schedule | Both: tools *and* scheduled context | -You pick the shape by including a `foreground`, `background`, or both sections in your `truffile.yaml`. +You pick the shape with `metadata.foreground`, `metadata.background`, or both +in `truffile.yaml`. Foreground apps can run Python tools or use a native remote +MCP connector. See [accounts and authentication](/sdk/accounts) for a complete +connector example and connection-scoped credentials. The supported development loop is intentionally CLI-first: scaffold locally, -validate locally, dry-run the deploy plan, then deploy to a connected device. +check the bundle locally, preview the upload, then deploy through the native +installer and builders. Use `--update` for later code changes that preserve +installed accounts. The bundled examples live under `truffile/app-store/` in the `truffile` repo. Treat them as reference implementations: @@ -48,7 +53,8 @@ from truffile.app_runtime import ( ) ``` -The source lives in `truffile/app_runtime/` in the repo and ships with the installed package. Once you `pip install truffile`, the same runtime is importable from app code running on your device. +The runtime is copied from PyFW into the release wheel; it is not tracked in +the SDK source checkout. It ships as `truffile.app_runtime` in the installed package. Once you `pip install truffile`, the same runtime is importable from app code running on your device. There are two other entry points that import the same names: @@ -84,7 +90,8 @@ Flags: - `truffile create --path ` — set name and base path - `truffile create` — fully interactive -The generated `my_app_foreground.py` and `my_app_background.py` are stubs using the real SDK classes. Delete whichever you don't need — the app type is inferred from which `foreground` / `background` sections you keep in `truffile.yaml`. +The foreground starter includes a ping tool. The background starter is a +placeholder that you must implement before relying on scheduled work. Delete whichever you don't need — the app type is inferred from which `foreground` / `background` sections you keep in `truffile.yaml`. --- @@ -104,9 +111,13 @@ The skill handles architecture decisions (foreground/background/hybrid, auth typ ## `truffile.yaml` structure -Every app needs a `truffile.yaml`. Minimum viable config: +Every app needs a `truffile.yaml`. This authenticated process example assumes +you implement the Python entrypoints, including a `--verify` mode that checks +the API key and exits. For a first ping test, use the generated scaffold as-is. ```yaml +truffile_version: 4 +connections: multi metadata: name: My App bundle_id: org.example.myapp @@ -136,17 +147,22 @@ metadata: steps: - name: Install dependencies type: bash + scope: install run: | pip install --no-cache-dir httpx - name: Copy app files type: files + scope: install files: - source: ./my_app_foreground.py + destination: ./my_app_foreground.py - source: ./my_app_background.py + destination: ./my_app_background.py - name: Configure access token type: text + scope: connection content: | Enter an API key from https://example.com/settings/tokens fields: @@ -177,7 +193,9 @@ Steps run during installation on the device, in order. The manifest supports: See the [Install steps reference](/sdk/install-steps) for every option on each type — field shapes, validators, exit code semantics, and multi-field examples. -`vnc` / browser setup steps are not supported through `truffile`. `truffile validate` rejects them with an explanation and tells users to install those apps through Symphony Settings. +Native deployment can present browser/VNC setup cards from firmware. Open the +shown setup URL and follow the app's flow. The explicit legacy builder supports +only its older step subset; use normal deploy for modern manifests. --- @@ -194,9 +212,14 @@ Pick the smallest auth surface that matches the upstream service: | Remote MCP OAuth | The upstream MCP server owns OAuth metadata or dynamic client registration | `oauth` with resource metadata / DCR fields | `notion` | | Local bridge | The app on the device needs to talk back to a process on the user's computer | usually `text`, often prefilled by a helper command | `obsidian` | -For `text` auth, add a validator that calls `python ./foreground.py --verify` or `python ./background.py --verify`. For OAuth, write a tiny auth helper around `OAuth` or `RemoteMcpOAuth`, store the token file path in an env var, and verify it in a follow-up `bash` step. +For `text` auth, implement a provider check and call it from a connection-scoped +validator. Native OAuth stores tokens for the selected connection. Custom Python +apps use runtime credential helpers; native connectors declare an OAuth +`app_var_key` matching their connection step. See the [account guide](/sdk/accounts) +for the complete connector recipe. Older bundled token-file examples should not +be copied into shared state for a multi-account app. -If an update reaches an auth step that cannot be refreshed non-interactively, fail clearly and tell the user to reauthenticate from Settings. +If an update reaches an auth step that cannot be refreshed non-interactively, fail clearly and ask the user to run `truffile connections reconnect "My App"`. --- @@ -208,11 +231,19 @@ truffile deploy --dry-run ./my-app truffile deploy ./my-app ``` -- **`validate`** checks the `truffile.yaml` structure, confirms referenced files exist, and parses all Python files for syntax errors. -- **`deploy --dry-run`** builds the deploy plan without changing anything on the device, so you can see what files will be uploaded, what bash steps will run, and how the process config will look. -- **`deploy`** opens a build session on the device, uploads files, runs your steps in order, and registers the app. +- **`validate`** checks bundle structure and basic metadata. Use `python -m compileall -q ./my-app` separately for Python syntax. +- **`deploy --dry-run`** packages and describes the bundle without changing the device. +- **`deploy`** sends the original manifest to firmware for semantic validation, builder execution and account setup. + +After editing app code, run `truffile deploy ./my-app --update`. This preserves +accounts, credentials and saved settings. `--replace` deliberately resets +accounts after confirmation. Current firmware is required for uploaded updates +and replacements; the CLI checks support before either operation. + +`--interactive` opens the explicit legacy builder's shell. Ordinary native +sign-in prompts do not require that flag. See [deployment](/sdk/deployment) +for packaging exclusions, timeouts, account selection and scripted setup. -Add `--interactive` to `deploy` to open a shell inside the app's container when the build steps finish — useful for installing extra packages or debugging before finalizing. After deployment, use an agent-safe one-shot request as a routing smoke test: diff --git a/sdk/cli.mdx b/sdk/cli.mdx index e301b99..c747606 100644 --- a/sdk/cli.mdx +++ b/sdk/cli.mdx @@ -22,14 +22,14 @@ Most workflows can be driven with explicit arguments, which makes them easy to test in CI or from another agent: ```bash -truffile connect truffle-1234 +truffile connect 6272 --user-id "$TRUFFLE_USER_ID" truffile list devices truffile list apps --json truffile load all truffile create my-app --path ./apps truffile validate ./apps/my-app truffile deploy ./apps/my-app --dry-run --json -truffile deploy ./apps/my-app --json --non-interactive --replace +truffile deploy ./apps/my-app --update --json --non-interactive truffile delete my-app truffile convo --new --json --quiet --timeout 60 "Summarize my installed apps" truffile infer "Say pong" --json --no-stream --quiet @@ -70,15 +70,15 @@ Select a device by number to start the connection flow. If you already know your device name: ```bash -truffile connect truffle-1234 +truffile connect 6272 --user-id "$TRUFFLE_USER_ID" ``` -Enter your Truffle username and password when prompted, then approve the connection on your device. +Enter your User ID from Symphony Settings when prompted, then approve the connection on your device. Credentials are saved locally — subsequent connections are automatic. -Before `truffile connect`, onboard the device in the Symphony desktop client. Download Symphony from [the client overview](/client/overview) and finish device onboarding there. The CLI will ask for your Truffle username and password before you approve the session on your device. +Before `truffile connect`, onboard the device in the Symphony desktop client. Download Symphony from [the client overview](/client/overview) and finish device onboarding there. The CLI will ask for your User ID from Symphony Settings before you approve the session on your device. --- @@ -390,41 +390,67 @@ truffile create my-app --path ./apps Creates a scaffold with `truffile.yaml`, foreground/background entry points, and a default icon. -### Validate an app +### Validate and deploy ```bash truffile validate ./my-app +truffile deploy ./my-app --dry-run +truffile deploy ./my-app ``` -Checks your `truffile.yaml` configuration and Python file syntax. +`validate` and `--dry-run` check bundle structure and basic manifest metadata. +Firmware validates full manifest semantics during installation. For Python +syntax, also run `python -m compileall -q ./my-app`. + +Normal deploy uploads your directory or `.truffle` bundle to the native +installer, which runs its builders and account setup. Text/password forms are +shown in the terminal; OAuth opens a sign-in flow with a callback/code handoff; +browser/VNC setup cards expose the device setup URL. Actual provider support +depends on the firmware and app's setup plan. -### Deploy an app +### Update or replace ```bash -# deploy from current directory -truffile deploy +truffile deploy ./my-app --update +truffile deploy ./my-app --replace +``` -# deploy from a path -truffile deploy ./my-app +Keep the same bundle ID. Update preserves accounts and settings. Replace runs +fresh setup and resets accounts after confirmation; scripts must add `--yes`. +Both require firmware with developer-upload support. Normal deploy refuses an +existing bundle ID until you choose a mode. + +`--legacy-builder` retains the old direct builder for fresh installations. +`--interactive` opens that legacy builder's debug shell. It is not the flag +for normal interactive sign-in and does not support native connectors. -# dry run — show the deploy plan without touching the device -truffile deploy --dry-run +### Accounts and permissions -# interactive mode — opens a terminal inside the app container -truffile deploy --interactive +```bash +truffile connections list "My App" --json +truffile connections add "My App" +truffile connections reconnect "My App" +truffile connections rename "My App" Work +truffile connections permissions "My App" Work read-only +truffile connections delete "My App" Work ``` -The deploy process: -1. Validates `truffile.yaml` and Python syntax -2. Resolves app shape (foreground, background, or both) -3. Uploads files and runs installation steps -4. Registers the app with the device +App names, bundle IDs and UUIDs work. Omit the account for a picker; the rename +example picks an account and calls it `Work`. Scripts must supply an unambiguous +account alias or UUID. `--json` and `--non-interactive` never prompt. -`oauth` steps print an authorization URL and ask for the pasted callback URL or code. `welcome` steps print their content and wait for Enter. `vnc` / browser setup steps are rejected by validation and should be installed through Symphony Settings. +Permission modes are `default`, `read-only`, `read-write`, and `auto-review`. +`default` removes an account override and inherits device policy. `--notes` +sets review notes; omitting it keeps saved notes. New accounts start with device +policy, and the CLI does not modify device-wide defaults. - -Use `truffile deploy --interactive` to open a shell inside your app's container. Useful for debugging, installing extra dependencies, or testing your app manually before finalizing. - +Supply `--answers-file /private/answers.json` to automate text/password setup. +Keep that JSON map of field names to values outside your app directory. OAuth +requires interactive approval; credentials remain with the device's installer. +On expiry or disconnect, inspect the installed app before retrying. + +See [deployment](/sdk/deployment) for all lifecycle options and +[accounts and authentication](/sdk/accounts) for authoring patterns. ### List apps and devices @@ -492,10 +518,13 @@ Both modes let you talk to models on your Truffle, but they serve different purp | `truffile connect ` | Connect to a device | | `truffile disconnect [device\|all]` | Disconnect and clear credentials | | `truffile create [name] [--path ]` | Scaffold a new app | -| `truffile validate [path]` | Validate app config and sources | +| `truffile validate [path]` | Check bundle structure and basic manifest metadata | | `truffile deploy [path]` | Deploy app to connected device | -| `truffile deploy --dry-run` | Show deploy plan without changes | -| `truffile deploy --interactive` | Deploy with interactive terminal | +| `truffile deploy --dry-run` | Inspect the bundle without changing the device | +| `truffile deploy [path] --update` | Upload code while preserving accounts | +| `truffile deploy [path] --replace` | Run fresh setup and reset accounts after confirmation | +| `truffile connections ` | List, add, reconnect, rename, delete, or set account permissions | +| `truffile deploy --interactive` | Open the legacy builder debug shell | | `truffile list apps` | List installed apps | | `truffile list devices` | List connected devices | | `truffile delete [all\|1 2 3]` | Delete apps (interactive, by index, or all) | diff --git a/sdk/deployment.mdx b/sdk/deployment.mdx new file mode 100644 index 0000000..90a58ff --- /dev/null +++ b/sdk/deployment.mdx @@ -0,0 +1,139 @@ +--- +title: Deploying and managing apps +description: "Install app bundles, update code, reconnect accounts, and control permissions with Truffile" +--- + +These commands are available in Truffile 0.3.35 and later. Upgrade with +`python -m pip install --upgrade truffile`. + +Normal `deploy` uploads the original bundle to firmware's native installer. +Firmware uses its existing builder containers to run the install plan. The CLI +handles progress, uploads, setup forms, and browser authorization. It does not +reimplement connection isolation or rewrite the manifest into a legacy plan. + +```bash +truffile connect 6272 +truffile validate ./my-app +truffile deploy ./my-app +truffile deploy ./my-app --update +truffile deploy ./my-app --replace +``` + +`deploy` accepts a directory containing `truffile.yaml` or a `.truffle` ZIP. +For a directory, files are packaged with stable ordering/timestamps. `.git`, +virtual environments, Python caches, `.env`/`.env.*`, and existing `.truffle` +archives are excluded. `.truffleignore` supports relative glob patterns and +directory prefixes, one per line. Symlinks, escaping archive paths and duplicate +archive entries are rejected. Bundles are limited to 200 MiB expanded. + +`validate` and `deploy --dry-run` perform local bundle preflight. The manifest +is passed through unchanged: firmware performs full schema and install-plan +validation, including connection scopes, connectors, static resources, base +image, and resource profile. A preflight pass is not proof that a particular +firmware supports every declared feature. + +## Install, update, replace + +| Command | Meaning | +| --- | --- | +| `deploy ./app` | Fresh installation. An existing matching bundle ID requires an explicit update or replace choice. | +| `deploy ./app --update` | Rebuild and publish code through firmware's existing developer update path, preserving installed account identities and setup. | +| `deploy ./app --replace` | Use the native reset path with the uploaded bundle; run fresh setup and replace the old accounts. The CLI confirms this reset. | +| `deploy ./app --replace --yes` | Explicitly confirm replacement for automation. | + +Both update and replacement require the PyFW developer-upload capability +available on current firmware. The CLI checks support +using a read-only request **before** starting either mutation. Older firmware +gets an upgrade-required error. It never falls back to deleting the app or to +a catalog reset. The server retains its existing behavior for older clients. + +Successful CLI deployment verifies the installed bundle hash after completion. +Keep the same `metadata.bundle_id` when updating an installed app. + +The direct builder remains available for development/debugging: + +```bash +truffile deploy ./legacy-app --legacy-builder +truffile deploy ./legacy-app --interactive +truffile validate ./legacy-app --legacy-builder +``` + +`--interactive` opens the legacy builder shell. Normal native deployment already +prompts for account setup; it does not need that flag. Direct builder deployment +supports the older schema and fresh bundle IDs only. Use normal deployment for +modern manifests, updates and replacements. + +## Accounts and permissions + +Friendly app names, bundle IDs and UUIDs are accepted. Account aliases and UUIDs +are accepted too. If a name is ambiguous, the terminal shows matching choices. + +```bash +truffile connections list Google +truffile connections add Google +truffile connections reconnect Google +truffile connections delete Google +truffile connections rename Google Work +truffile connections permissions Google read-only +``` + +For reconnect/delete/rename/permissions, omitting the account opens a numbered +picker. The rename example chooses an account and names it `Work`; the permission +example chooses an account and makes it read-only. To select directly: + +```bash +truffile connections reconnect Google "work@example.com" +truffile connections rename Google "work@example.com" Work +truffile connections permissions Google Work auto-review --notes "Review external writes" +truffile connections delete Google Work --yes +``` + +Deletion asks for confirmation unless `--yes` is provided. Scripts must provide +an unambiguous account and `--yes` for deletion. `--json` and `--non-interactive` +never open a picker or prompt. No selection silently means “all accounts.” + +Permission modes are `default`, `read-only`, `read-write`, and `auto-review`. +`default` removes the account override so device policy applies. Omitted +`--notes` preserves the current review notes. These commands do not alter +device-wide defaults or grant an app permission automatically. + +## Authentication + +Native text/password forms, including API-key setup, are rendered in the +terminal. Password inputs are hidden. For automation, supply a JSON map of +field names to string values: + +```bash +truffile deploy ./app --non-interactive --json --answers-file /private/setup.json +truffile connections add "My App" --json --answers-file /private/second-account.json +``` + +Keep answer files outside the source tree. The explicitly supplied answer file +is excluded from directory uploads even if it is inside the tree. Never include +credentials in a prebuilt bundle. Field keys can be `name` or `step_index.name` +when separate steps use the same field name. Supplied values are not printed. + +For OAuth, the CLI shows the authorization URL and accepts a callback URL or +provider approval code. URL callbacks must match the pending state. On POSIX, +firmware-provided localhost callbacks can complete automatically; pasting is +also available when the browser is on another computer or the port is busy. +Browser/VNC setup cards show the device setup URL and follow the card's completion +behavior. Native device-code approval displays the link and waits for the +provider process to finish without asking for a callback paste. +Provider-specific consent, token refresh and custom setup still run +through the firmware install plan, not through a new CLI token store. + +Interactive authorization is not silently completed in JSON/noninteractive +mode. Missing input returns an interaction-required error and requests +cancellation. Rerun interactively or supply form answers as appropriate. + +`--timeout` defaults to 900 seconds for install/add/reconnect. A timeout or +disconnect is not permission to retry blindly: cancellation is requested, but +the operation may already have committed. Inspect the app before retrying. +The device can expire sign-in before the CLI's deadline; increasing `--timeout` +does not extend that authorization window. The CLI continues receiving device +responses while prompting, closes stale prompts/callback listeners on expiry or +disconnect, and reports the device's error. An expired setup needs a new sign-in +link. +Mutations are serialized on firmware, so an immediate follow-up command can +report that another operation is still finishing. diff --git a/sdk/install-steps.mdx b/sdk/install-steps.mdx index 92d7ff2..686e640 100644 --- a/sdk/install-steps.mdx +++ b/sdk/install-steps.mdx @@ -15,6 +15,7 @@ steps: type: files files: - source: ./my_app_foreground.py + destination: ./my_app_foreground.py - name: Configure type: text fields: @@ -24,17 +25,36 @@ steps: env: MY_APP_API_KEY ``` -Valid app manifest step types are **`bash`**, **`files`**, **`text`**, **`oauth`**, and **`welcome`**. Browser/VNC steps are rejected by `truffile validate`. +Common native steps are **`bash`**, **`files`**, **`text`**, **`oauth`**, and +**`welcome`**. Firmware also supports provider-specific browser/VNC setup plans. +Normal deployment uploads the original manifest and lets firmware validate it. -The local CLI builder supports `oauth` with a model-friendly flow: it prints the authorization URL, accepts a pasted callback URL or authorization code, exchanges it for tokens, writes the token file into the app container, and injects the token file env var into later build steps and the final app process. If an update reaches an auth step that cannot complete non-interactively, fail clearly and ask the user to reauthenticate from Settings. +For new authenticated apps, use connection-scoped setup and credentials. See +[accounts and authentication](/sdk/accounts). Normal CLI deployment relays +forms and OAuth callbacks to firmware; firmware performs token exchange and +storage. The explicit `--legacy-builder` path retains older token-file recipes. +## Installation scope and account scope + +Use `truffile_version: 4` for a process-based account-aware manifest, or version +5 for a native connector. Declare `connections: multi` when the app supports +multiple accounts. Mark shared dependencies/files with `scope: install`, and +authentication/configuration with `scope: connection`. The connection plan runs +for first setup, adding an account, and reconnecting an account. + +An uploaded `--update` preserves existing accounts. Keep credentials out of +install-scoped files and app-global variables. Legacy `update_policy` and +`update_check` examples below describe older recipes; do not substitute them +for connection scope in a new multi-account app. + + --- ## Step ordering -Steps execute top-to-bottom. The three practical rules: +Steps execute top-to-bottom. The practical rules: 1. **Install dependencies before anything that needs them.** Put your `bash` step that runs `pip install` early. 2. **Copy files before running them.** Any `bash` step or `text` validator that invokes a Python file needs a preceding `files` step that uploads it. @@ -334,7 +354,9 @@ if not api_key: ## `oauth` -Runs an OAuth authorization flow during install and writes the resulting token payload to a file in the app container. Use it when the provider requires browser authorization instead of a pasted API key. +Runs OAuth authorization for the connection being configured. Firmware stores +native token state for that connection. Older process-based recipes also use a +token file in the app container. Use it when the provider requires browser authorization instead of a pasted API key. There are two real patterns in the bundled examples: @@ -405,9 +427,9 @@ There are two real patterns in the bundled examples: | `resource_metadata_endpoint` | remote MCP | OAuth protected-resource metadata URL. | | `oauth_resource` | remote MCP | Resource audience for the remote MCP server. | | `client_name` | remote MCP | Client display name used during DCR. | -| `token_output_file` | yes | Path where the installer writes token JSON in the app container. | -| `token_file_env_name` | yes | Env var exposed to the app process with the token file path. | -| `app_var_key` | yes | App-scoped key used to persist OAuth state. | +| `token_output_file` | legacy/process recipes | Optional token-file compatibility path; native connectors use connection-scoped token state. | +| `token_file_env_name` | with a token file | Env var exposed to a Python app with its token-file path. | +| `app_var_key` | native OAuth | Credential key within the selected connection; match the connector or runtime helper configuration. | | `update_check` | no | Verify command to avoid unnecessary reauth when tokens still work. | ### App-side OAuth helper @@ -416,7 +438,7 @@ In Python, subclass `OAuth` for normal providers or `RemoteMcpOAuth` for remote The `whoop` example uses `WhoopOAuth(OAuth)` and stores tokens at `WHOOP_TOKEN_STORE_PATH`. The `notion` example uses `NotionAuth(RemoteMcpOAuth)` and stores tokens at `NOTION_OAUTH_TOKEN_FILE`. -For public/dynamically registered remote MCP clients, the CLI uses PKCE automatically when no client secret is available. Token JSON also includes refresh metadata such as `client_id`, `client_secret` when present, `redirect_uri`, `token_endpoint`, and `resource`, so app-side helpers can refresh later without needing separate env vars. +For public/dynamically registered remote MCP clients, the OAuth installer uses PKCE automatically when no client secret is available. Token JSON also includes refresh metadata such as `client_id`, `client_secret` when present, `redirect_uri`, `token_endpoint`, and `resource`, so app-side helpers can refresh later without needing separate env vars. --- @@ -441,12 +463,13 @@ The `viator` example uses this pattern after a `bash` verify step because its up ## Validating before you deploy -`truffile validate` catches most step-level problems before you ever hit the device: - -- Unknown step types are rejected with a clear message that lists the supported manifest types. -- `vnc` / browser setup steps are rejected with a clear message telling the user to install through Symphony Settings. -- Missing required fields (`type`, `run` on bash, `files` on files) produce errors. -- Missing `source` files in `files` steps are caught. -- Python syntax errors in any uploaded `.py` file are caught. +Run `truffile validate ./my-app` and `truffile deploy ./my-app --dry-run` for +local bundle preflight. These check basic metadata, archive paths and packaging +limits; they do not reproduce firmware's complete schema validator or execute +provider checks. Run `python -m compileall -q ./my-app` for Python syntax and +your app's tests for client behavior. Firmware validates the full manifest and +install plan when you deploy. -Run `truffile validate ./my-app` before every `truffile deploy` — it's nearly instant and catches everything except runtime failures in your `bash` commands and `text` validators. +For an older manifest that explicitly uses the direct builder, +`truffile validate ./my-app --legacy-builder` retains the older schema and +source checks. That validator does not support modern connector/browser plans. diff --git a/sdk/installation.mdx b/sdk/installation.mdx index 2d1cf4f..98ace99 100644 --- a/sdk/installation.mdx +++ b/sdk/installation.mdx @@ -26,7 +26,7 @@ Requirements: - Install Truffile with `pip install truffile`; if it is already installed, make sure it is upgraded to the latest version. - Verify the install with `truffile --help`. - Run `truffile load all` so the bundled skills and example apps are copied into this workspace under `./truffile/skills` and `./truffile/examples`. -- Do not require a Truffle device to finish setup. If no device is connected yet, tell me that I first need to onboard my Truffle through the Symphony desktop client at https://docs.truffle.net/client/overview, then run `truffile scan` and `truffile connect `. During `truffile connect`, I can enter my Truffle username and password, then approve the new session on my Truffle device. +- Do not require a Truffle device to finish setup. If no device is connected yet, tell me that I first need to onboard my Truffle through the Symphony desktop client at https://docs.truffle.net/client/overview, then run `truffile scan` and `truffile connect `. During `truffile connect`, I can enter my User ID from Symphony Settings, then approve the new session on my Truffle device. After `truffile load all`, inspect the local resources before giving me the onboarding: - Read `./truffile/skills/truffile-cli/SKILL.md` for scan/connect/create/validate/deploy/list/delete/obsidian. @@ -37,7 +37,7 @@ After `truffile load all`, inspect the local resources before giving me the onbo - Run `truffile --help`, `truffile deploy --help`, `truffile convo --help`, and `truffile infer --help` so you know the CLI surface. When setup is complete, give me a short onboarding that covers: -- that connecting requires a Symphony-onboarded Truffle, my Truffle username and password, and device approval during `truffile connect` +- that connecting requires a Symphony-onboarded Truffle, my User ID from Symphony Settings, and device approval during `truffile connect` - how to connect a Truffle device - how to use Convo with the on-device agent, including that old Task histories are not visible after the one-way cutover - how to use Truffile as an inference endpoint/client @@ -135,13 +135,32 @@ The prompt works because Truffile is CLI-first: every core workflow has explicit With your environment activated: ```bash -pip install truffile +python -m pip install --upgrade truffile ``` -Before connecting from Truffile, onboard your device through the Symphony desktop client. Download Symphony from [the client overview](/client/overview) and complete device onboarding. When you run `truffile connect`, enter your Truffle username and password, then approve the new session on your Truffle device. +Before connecting from Truffile, onboard your device through the Symphony desktop client. Download Symphony from [the client overview](/client/overview) and complete device onboarding. When you run `truffile connect`, enter your User ID from Symphony Settings, then approve the new session on your Truffle device. +## Build and manage apps + +Truffile 0.3.35 adds native installation, account setup, developer uploads, and +friendly account selection. Use current device firmware for uploaded updates +and replacements; unsupported firmware is detected before those operations. + +```bash +truffile connect 6272 +truffile create my-app --path ./apps +truffile validate ./apps/my-app +truffile deploy ./apps/my-app +truffile deploy ./apps/my-app --update +``` + +Read [deployment](/sdk/deployment) and [accounts and authentication](/sdk/accounts) +for updates, replacement, OAuth, API keys, and permission controls. After upgrading, +refresh previously exported skills with `truffile load skills --force` only if +you intend to overwrite those exported copies; keep your app source separately. + ## Next steps