Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,17 @@ ENGRAPHIS_LLM_API_KEY=sk-your-key-here
# ENGRAPHIS_CLOUD_COMPUTE_URL=https://compute.engraphis.com
# ENGRAPHIS_CLOUD_ORGANIZATION_ID=org_replace_me

# Prefer the owner-only ~/.engraphis/cloud_session.json created during hosted onboarding.
# Prefer the owner-only ~/.engraphis/cloud_session.json written by `engraphis connect`:
#
# engraphis connect --token engr_ct_... # the command your account portal shows
# printf %s "$TOKEN" | engraphis connect --token - # keep the token out of shell history
#
# That is the supported way to connect a client. It redeems the one-time connect token, saves
# the rotating refresh credential with 0600 permissions, and keeps it rotated afterwards, so
# none of the variables below are needed on an interactive machine. Set
# ENGRAPHIS_CLOUD_COMPUTE_URL (or pass --compute-url) only if your account portal shows a
# compute endpoint different from the default. See docs/AGENT_CONNECT.md.
#
# For non-interactive deployments a refresh credential may be injected as a bootstrap secret.
# It rotates on use; the owner-only saved replacement takes precedence afterward, even while
# the environment variable remains set. Never commit it. Bind environment-only bootstrap
Expand Down
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,32 @@ All notable changes to Engraphis are documented here. Format loosely follows

## [Unreleased]

### Added

- **`engraphis connect --token engr_ct_…`** — the missing client half of device connect.
`cloud_session.save_bootstrap()` is the only writer of `~/.engraphis/cloud_session.json`,
and it had no production caller: the docs told paying customers to prefer a file nothing
created, so a purchased installation could not be connected without hand-writing state.
The new command redeems the one-time connect token from the account portal against
`POST /v1/devices/connect`, saves the returned session with owner-only permissions, and
verifies `cloud_session.configured()` before reporting success. The token is sent in the
request body and nowhere else — never printed, logged, or written to disk — and every
refusal maps to fixed, actionable copy (an expired or already-used token is not confused
with a lapsed subscription). Session storage is pre-flighted before the exchange, so an
unwritable state directory or a `cloud_session.json` replaced by a link fails the command
*without* spending the single-use token — the customer fixes the path and retries with the
same token instead of returning to the portal for a new one. Faults that can only happen
*after* the exchange — a reply truncated mid-body (`http.client.IncompleteRead`), or an
endpoint that stops resolving before the session is written (`CloudUrlUnresolved`) — are
reported as errors that say the token was already used, rather than escaping as tracebacks
that leave the customer unable to tell whether to retry. Also installed as
`engraphis-connect`.
- An `engraphis` front-door command that dispatches to the existing `engraphis-<verb>`
entry points, so the command the account portal displays is runnable as shown.
- A stable per-installation identity at `~/.engraphis/client_identity.json` (random ULIDs,
not a hardware fingerprint) so reconnecting a machine updates its existing installation
instead of registering a new device every time.

### Removed

- **"Signed compliance export" is no longer advertised on any surface.** It was granted to
Expand Down
57 changes: 54 additions & 3 deletions docs/AGENT_CONNECT.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ organization:

1. The organization owner starts Team or purchases a subscription in Engraphis Cloud.
2. The owner invites named members and assigns roles in the hosted dashboard.
3. A member accepts the invitation and creates a scoped agent/device credential.
4. The member configures their agent with the hosted URL and the one-time credential.
3. A member accepts the invitation and generates a one-time **connect token** (`engr_ct_…`).
The account portal shows the exact command to run.
4. The member runs that command on the machine being connected (see below).
5. The hosted service rechecks organization membership, role, scopes, entitlement version, and
workspace binding on every request.

Expand All @@ -37,6 +38,53 @@ The hosted onboarding flow provides the exact endpoint and client snippet for th
organization. Do not substitute the URL of a public self-hosted image: that image intentionally
has no Team identity backend.

## Connect a machine: `engraphis connect`

Copy the command from your account portal and run it on the machine you are connecting:

```bash
engraphis connect --token engr_ct_...
```

That redeems the token against `POST /v1/devices/connect` on the control plane and writes the
owner-only session file `~/.engraphis/cloud_session.json` (mode `0600`). The dashboard, the MCP
server, and Cloud Sync all read that file, so no environment secret is needed afterwards. Rerun
`engraphis connect` with a fresh token on every machine you want connected.

Useful options:

| Option | Effect |
| --- | --- |
| `--token -` | Read the token from stdin, so it never enters shell history. |
| `--workspace WS_ID` | Bind this device to a single workspace. |
| `--label TEXT` | Name this installation in your account portal. |
| `--device-name TEXT` | Override the device name (defaults to the hostname). |
| `--control-url URL` | Point at a non-default control plane. |
| `--compute-url URL` | Set the managed compute endpoint (also `ENGRAPHIS_CLOUD_COMPUTE_URL`). |
| `--json` | Print a redacted, machine-readable summary. |

The same command is installed as `engraphis-connect`, matching the other `engraphis-*` scripts.

Connect tokens are **single-use and short-lived**. The service answers every refusal — expired,
already redeemed, or never valid — with the same `401`, so the client reports all three
possibilities and the fix is always the same: generate a new token in the account portal. A `402`
means the subscription itself has lapsed; fix billing rather than the token.

Because the token is single-use, the client checks that it can actually write the session file
*before* redeeming it. If the state directory is not writable, or `cloud_session.json` has been
replaced by a symlink, a hard link, or a directory, the command fails immediately, names the path
to fix, and sends nothing — your token is untouched, so you can correct the path and rerun the
same command rather than issuing a new token.

The token is a credential. It is sent in the request body and nowhere else — it is never
printed, never logged, and never written to disk. What *is* written is the rotating refresh
credential the service returns, which is why the session file is owner-only.

The command also mints a stable per-installation identity at `~/.engraphis/client_identity.json`
(random ULIDs, not a hardware fingerprint) so reconnecting the same machine updates the existing
installation instead of registering a new device every time. Both files honour
`ENGRAPHIS_STATE_DIR`; a distinct state directory is a distinct installation.

## Credential lifecycle

Hosted access uses short-lived access tokens plus rotating refresh credentials. A refresh family
Expand All @@ -45,7 +93,10 @@ service; the raw replacement is returned once and must be kept in an owner-only
or secrets manager.

Customer-side environment variables are documented in [`.env.example`](../.env.example). Prefer
the onboarding-created `~/.engraphis/cloud_session.json` over long-lived environment secrets.
the `~/.engraphis/cloud_session.json` that `engraphis connect --token` writes over long-lived
environment secrets: it holds a rotating credential, it is owner-only, and it is the path the
client keeps up to date on its own. Environment secrets are for non-interactive deployments that
cannot run the connect command.

## Trial and grace

Expand Down
Loading
Loading