Skip to content
Open
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
39 changes: 39 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,45 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.12.9] - 2026-11-04

**Claude Code and other coding agents can call altimate-code's data tools directly.** `altimate-code mcp serve` and the new model-free `altimate-code tool` CLI run the same tools without a second model: reads by default, writes only with `--allow-write` (over MCP, `warehouse_add` can save a connection without it). In projects linked to a workspace, what people teach the agent can become a team rule: they confirm it on a card, the workspace owner reviews it, and approved rules reach every teammate's sessions. Also in this release: `auth logout` removes the saved Altimate gateway key, the analyst stays read-only whatever config grants, and the home screen suggests the Claude Code and Codex plugins.

**Heads-up for support:**
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Resolved
- **MCP and `tool run` requests are read-only unless `--allow-write`.** The database enforces read-only on PostgreSQL, SQLite and DuckDB; on other warehouses it rests on SQL classification, so stored functions with side effects are not blocked. `DROP DATABASE`, `DROP SCHEMA` and `TRUNCATE` are refused even with `--allow-write`, but that block also reads the SQL text and is best effort. Use a read-only database user for a hard guarantee. `--allow-write` is all or nothing, and statements such as `SET`, `ATTACH`, `COPY ... TO`, `INSTALL` and DuckDB `SUMMARIZE` count as writes.
- **`mcp serve` allows `warehouse_add` without `--allow-write`**, so a calling agent can save a connection; `tool run` needs `--allow-write` for it. Tool results, including query rows and sample values, go to the calling agent's model provider. Large results are spilled to private local files.
- **`tool run` starts a per-project background process** that keeps connections warm and exits after 10 idle minutes. `--no-daemon` runs a call in its own process, and `ALTIMATE_TOOL_DAEMON=0` turns the background process off.
- **Workspace skill syncs can write copies into `<project>/.claude/skills/`** when `ALTIMATE_CLAUDE_SKILL_SYNC=1` is set, which the Claude Code plugin does. Skills you wrote with the same name are never touched; unlinking or signing out removes the copies.
- **`schema_inspect` without a table** lists schemas, or up to 500 tables with catalog row estimates, instead of scanning.
- **`learn status` prints `Learning from sessions: on|off (source)`** where it printed `Capture:`, and learn messages say "rule" where they said "lesson". The JSON output only gains a `rules` block.
- **In a linked project, `training_save` with `kind: "rule"` shows a proposal card** instead of writing training, and `learn bootstrap` / `learn import-reviews` send what they find for review.
- **Telemetry carries the signed-in user ID and tenant** when the machine is signed in to Altimate, so those events are not anonymous; no email address, hashed or not, is sent. One `GET /users/me` lookup per credential finds them, and none is made while telemetry is off. Rows from machines that are not signed in keep the machine id. Turning telemetry off sends one final `telemetry_opted_out` event (machine id and opt-out source only), then nothing.
- **The analyst's built-in write tools stay off whatever config grants**, including `"*": "allow"`: file edits, SQL writes, memory saves, shell and delegation. Configs that relied on a grant to give the analyst more power will see those tools denied. `task` from the analyst is limited to `data-explorer`; MCP and custom tools still follow config.
- **A symlink inside an allowed external folder that points elsewhere now asks** for the folder it points to, for every agent.
- **`auth logout` removes the saved gateway key**, `~/.altimate/altimate.json`, MCP server entries that carry that key (add the datamate again after signing in), and the cached telemetry identity.
- **Ctrl+C during browser sign-in exits with status 130**, not 0.
- **The Altimate LLM Gateway free-token allowance is 1M tokens.**

### Added

- **`altimate-code tool` CLI and expanded `mcp serve`.** `tool list`, `schema`, `run`, `context` and `daemon` call the same tools as `mcp serve` through one adapter, one allow-list and one permission layer. Default MCP tools now include `impact_analysis`, `dbt_lineage`, `altimate_core_column_lineage`, `schema_detect_pii`, `data_diff`, `altimate_core_equivalence` and `sql_translate`; `--all-tools` exposes the full list. Workspace tools are on for `tool` commands in linked projects (`--no-workspace-tools` turns them off) and opt-in for `mcp serve` (`--workspace-tools`). `link --claude-md` writes routing guidance into `CLAUDE.md`. (#1609)
- **Workspace rules.** A proposal card in the TUI (`ctrl+x p`), owner review with approve, edit and "Not a rule", `/rules` and a sidebar `rules:` line, `learn sync`, and approved rules delivered into every session with their `applies_to` paths. See "Workspace rules" in the learn docs. (#1605)
- **Plugin tips on the home screen** for Claude Code and Codex, and a README section on using altimate-code from them. (#1625)
- **Workspace telemetry** for linking, usage, the engine and the starter. The telemetry reference lists every new event. (#1513)
- **Signed-in identity on telemetry and a final opt-out event.** (#1501)

### Changed

- **The analyst stays read-only.** After config, it denies again every built-in tool that changes state, so a wildcard or explicit grant cannot turn them back on, and it can read the files that ship with global skills. (#1597)
- **External-directory checks follow symlinks**, so a link in an allowed folder cannot reach a folder that is not allowed. (#1597)
- **The analyst asks for high reasoning effort** on adaptive-thinking models unless the user or agent config sets one. (#1609)

### Fixed

- **`auth logout` removes the saved Altimate gateway key** and the MCP entries that carry it. Sign-out now also removes the cached telemetry identity, as the telemetry reference says. (#1606)
- **Ctrl+C during browser sign-in exits 130** instead of reporting success. (#1624)
- **Docs and the TUI show the 1M free-token allowance.** (#1611)

## [0.12.8] - 2026-10-23

**Claude Code can work through your Altimate workspace.** With the Altimate Code plugin installed and the project linked, tasks Claude hands to altimate-code run with the workspace's skills, knowledge and shared memory, and a headless `run` no longer hangs waiting on a stdin nobody writes to. Also in this release: the curl and PowerShell installers add an `altimate-code` command, the analyst can read workspace memory, `learn status` shows whether a lesson keeps being corrected, `schema_inspect` shows what column values look like, and SQL policy checks fail closed on statements they cannot analyze.
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/configure/mcp-servers.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Add this entry to your MCP client's configuration (for example, Claude Code's `.
}
```

By default the server exposes `warehouse_list`, `warehouse_add`, `sql_execute`, `sql_materialize` and `answer_commit`, plus `impact_analysis`, `dbt_lineage`, `altimate_core_column_lineage`, `schema_detect_pii`, `data_diff`, `altimate_core_equivalence` and `sql_translate`. Pass `tables` to `sql_execute` to get those tables' columns and value profiles in the same call as its queries. Start with `--all-tools` to expose every data tool, the same set `altimate-code tool list` shows. Without `--connections`, the server reads the usual connection sources and re-reads them while none are configured, so a connections file written mid-session, or a connection added with `warehouse_add`, is picked up without a restart. A connections file is a JSON object keyed by connection name:
By default the server exposes `warehouse_list`, `warehouse_add`, `sql_execute`, `sql_materialize` and `answer_commit`, plus `impact_analysis`, `dbt_lineage`, `altimate_core_column_lineage`, `schema_detect_pii`, `data_diff`, `altimate_core_equivalence` and `sql_translate`. Pass `tables` to `sql_execute` to get those tables' columns and value profiles in the same call as its queries. Start with `--all-tools` to expose every data tool, the same set `altimate-code tool list` shows. In a project linked to a workspace, `--workspace-tools` adds `workspace_context`, `workspace_skill` and `workspace_execute`; in an unlinked project `workspace_context` reports what setup is missing. Without `--connections`, the server reads the usual connection sources and re-reads them while none are configured, so a connections file written mid-session, or a connection added with `warehouse_add`, is picked up without a restart. A connections file is a JSON object keyed by connection name:

```json
{
Expand All @@ -37,7 +37,7 @@ At startup the server lists each connection's name and type in its instructions,

<!-- altimate_change start — MCP read-only limits and private output lifecycle -->

Source writes are refused by default. Without `--allow-write`, source queries request database-enforced read-only execution on PostgreSQL, SQLite and DuckDB. For other connection types, read-only is enforced by SQL classification only; stored functions with side effects are not blocked — use a read-only database user. The server prints this caveat on stderr at startup and includes it in `warehouse_list` for each affected connection. Add `--allow-write` to enable SQL write permissions; existing hard safety blocks still apply (`DROP DATABASE`, `DROP SCHEMA` and `TRUNCATE` stay blocked). `warehouse_add` may write local connection configuration (the connections file and the operating system credential store) even in read-only mode. `sql_materialize` always requires a read-only source query and writes only into the server session's temporary `scratch` warehouse. Scratch tables disappear when the server exits. Large results may include a local spill file path that your client can open with any file reader. Spill files use a private directory and are deleted when the server exits. Local `tool_output.max_lines` and `tool_output.max_bytes` settings control output limits; remote agent configuration is not loaded.
Source writes are refused by default. Without `--allow-write`, source queries request database-enforced read-only execution on PostgreSQL, SQLite and DuckDB. For other connection types, read-only is enforced by SQL classification only; stored functions with side effects are not blocked — use a read-only database user. The server prints this caveat on stderr at startup and includes it in `warehouse_list` for each affected connection. Add `--allow-write` to enable SQL write permissions; `DROP DATABASE`, `DROP SCHEMA` and `TRUNCATE` are still refused. That block reads the SQL text, so it is best effort; use a database user without those privileges for a hard guarantee. `warehouse_add` may write local connection configuration (the connections file and the operating system credential store) even in read-only mode. `sql_materialize` always requires a read-only source query and writes only into the server session's temporary `scratch` warehouse. Scratch tables disappear when the server exits. Large results may include a local spill file path that your client can open with any file reader. Spill files use a private directory and are deleted when the server exits. Local `tool_output.max_lines` and `tool_output.max_bytes` settings control output limits; remote agent configuration is not loaded.

<!-- altimate_change end -->

Expand Down
8 changes: 4 additions & 4 deletions docs/docs/reference/security-faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export ALTIMATE_CLI_MODELS_PATH=/path/to/models.json

## What telemetry is collected?

Anonymous usage telemetry, including event names, token counts, timing, and error types. **Never** code, queries, credentials, file paths, or prompt content. See the full [Telemetry reference](telemetry.md) for the complete event list.
Usage telemetry, including event names, token counts, timing, and error types. **Never** code, queries, credentials, file paths, or prompt content. See the full [Telemetry reference](telemetry.md) for the complete event list.

Disable telemetry entirely:

Expand All @@ -164,8 +164,8 @@ export ALTIMATE_TELEMETRY_DISABLED=true

### How does Altimate Code identify users for analytics?

- **Logged-in users:** Your email is SHA-256 hashed before sending. We never see your raw email.
- **Anonymous users:** A random UUID (`crypto.randomUUID()`) is generated on first run and stored at `~/.altimate/machine-id`. This is NOT tied to your hardware, OS, or identity — it's purely random.
- **Signed-in users:** Events carry your Altimate user ID and tenant, so they are not anonymous. No email address, hashed or not, is sent. See [User identification](telemetry.md) for how the ID is looked up and cached.
- **Users who are not signed in:** A random UUID (`crypto.randomUUID()`) is generated on first run and stored at `~/.altimate/machine-id`. This is NOT tied to your hardware, OS, or identity — it's purely random.
- **Both identifiers** are only sent when telemetry is enabled. Disable with `ALTIMATE_TELEMETRY_DISABLED=true` or the `telemetry.disabled` config option.
- **No fingerprinting:** We do not use browser fingerprinting, hardware IDs, MAC addresses, or IP-based tracking.
- **CLI auth flow:** When you sign in via `altimate auth login`, the anonymous machine ID is included in the authorization URL and associated with your account in product analytics for funnel analysis. This is suppressed when you disable telemetry — via `ALTIMATE_TELEMETRY_DISABLED=true` or the `telemetry.disabled` config option — and the machine ID is omitted from the URL entirely.
Expand All @@ -184,7 +184,7 @@ No code, queries, file paths, or personal information is included. This event he
The install scripts (`altimate.sh/install`, `install.ps1`), the npm postinstall, and the VS Code extension's installer send nothing themselves and contact no telemetry endpoint. They only record the version and installer name to a local file that the CLI reads on its next run, so the opt-out above decides whether anything is ever transmitted.

!!! warning "One caveat on the config-file opt-out"
The environment variables (`ALTIMATE_TELEMETRY_DISABLED`, `OPENCODE_DISABLE_TELEMETRY`) are always honoured. The `telemetry.disabled` **config key** is read during telemetry startup, which can run before the CLI's config is resolvable — and in that case startup currently proceeds with telemetry enabled. A user who has opted out via the config key alone may therefore still have this event transmitted. Use an environment variable if you need a guarantee.
The environment variables (`ALTIMATE_TELEMETRY_DISABLED`, `OPENCODE_DISABLE_TELEMETRY`) are always honoured. The `telemetry.disabled` **config key** is read from the config files at telemetry startup. If a config file cannot be read or parsed at that point, startup proceeds with telemetry enabled, so this event may still be transmitted. Use an environment variable if you need a guarantee.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This caveat now describes telemetry staying on when the config file is unreadable as the settled behavior. The team guidance for telemetry.disabled is to fail closed, so either keep the wording marked as a known gap or fix startup to fail closed and update this text to match.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/docs/reference/security-faq.md, line 187:

<comment>This caveat now describes telemetry staying on when the config file is unreadable as the settled behavior. The team guidance for `telemetry.disabled` is to fail closed, so either keep the wording marked as a known gap or fix startup to fail closed and update this text to match.</comment>

<file context>
@@ -184,7 +184,7 @@ No code, queries, file paths, or personal information is included. This event he
 
 !!! warning "One caveat on the config-file opt-out"
-    The environment variables (`ALTIMATE_TELEMETRY_DISABLED`, `OPENCODE_DISABLE_TELEMETRY`) are always honoured. The `telemetry.disabled` **config key** is read during telemetry startup, which can run before the CLI's config is resolvable — and in that case startup currently proceeds with telemetry enabled. A user who has opted out via the config key alone may therefore still have this event transmitted. Use an environment variable if you need a guarantee.
+    The environment variables (`ALTIMATE_TELEMETRY_DISABLED`, `OPENCODE_DISABLE_TELEMETRY`) are always honoured. The `telemetry.disabled` **config key** is read from the config files at telemetry startup. If a config file cannot be read or parsed at that point, startup proceeds with telemetry enabled, so this event may still be transmitted. Use an environment variable if you need a guarantee.
 
 ## What happens when I authenticate via a well-known URL?
</file context>


## What happens when I authenticate via a well-known URL?

Expand Down
4 changes: 3 additions & 1 deletion docs/docs/usage/tool.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ Only the data tools are exposed. Coding tools (`bash`, `read`, `edit`, ...), `db

Runs are read-only by default. Reads such as `sql_execute` on a SELECT and `schema_inspect` are allowed; anything else is refused with `Permission denied: <kind>. Re-run with --allow-write to enable writes.` Tools that change local configuration (`warehouse_add`, `warehouse_remove`, `warehouse_install_driver`) also need `--allow-write`.

`DROP DATABASE`, `DROP SCHEMA` and `TRUNCATE` stay blocked even with `--allow-write`.
`DROP DATABASE`, `DROP SCHEMA` and `TRUNCATE` are refused even with `--allow-write`. The block reads the SQL text, so it is best effort: statements built at run time, for example by a stored procedure, are not seen. For a hard guarantee, connect with a database user that lacks `DROP` and `TRUNCATE`.

Tool results go to the model of the agent that called the tool. `sql_execute` returns result rows, `warehouse_list` shows one sample row per table of a local database, and `schema_inspect` and `sql_execute` with `tables` return value profiles per column; set `ALTIMATE_DISABLE_SCHEMA_VALUES=1` to leave the value profiles out. Keep passwords and keys out of `warehouse_add` arguments, which end up in the calling agent's transcript: reference credentials from a connections file or `ALTIMATE_CODE_CONN_*` variables instead.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The value-profile sentence leaves out what a profile contains. State that columns with at most 20 distinct values list them all, text columns above that show frequent values (and shapes for short text), and other columns show only distinct count and null share.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/docs/usage/tool.md, line 56:

<comment>The value-profile sentence leaves out what a profile contains. State that columns with at most 20 distinct values list them all, text columns above that show frequent values (and shapes for short text), and other columns show only distinct count and null share.</comment>

<file context>
@@ -51,7 +51,9 @@ Only the data tools are exposed. Coding tools (`bash`, `read`, `edit`, ...), `db
-`DROP DATABASE`, `DROP SCHEMA` and `TRUNCATE` stay blocked even with `--allow-write`.
+`DROP DATABASE`, `DROP SCHEMA` and `TRUNCATE` are refused even with `--allow-write`. The block reads the SQL text, so it is best effort: statements built at run time, for example by a stored procedure, are not seen. For a hard guarantee, connect with a database user that lacks `DROP` and `TRUNCATE`.
+
+Tool results go to the model of the agent that called the tool. `sql_execute` returns result rows, `warehouse_list` shows one sample row per table of a local database, and `schema_inspect` and `sql_execute` with `tables` return value profiles per column; set `ALTIMATE_DISABLE_SCHEMA_VALUES=1` to leave the value profiles out. Keep passwords and keys out of `warehouse_add` arguments, which end up in the calling agent's transcript: reference credentials from a connections file or `ALTIMATE_CODE_CONN_*` variables instead.
 
 ## Daemon
</file context>


## Daemon

Expand Down
4 changes: 2 additions & 2 deletions docs/mcp-workspaces.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Workspaces for external coding clients (experimental)

`altimate-code mcp serve --workspace-tools` adds workspace context, named skills and known read operations from the linked workspace's existing `datamate` engine. The default MCP server remains unchanged. This draft builds on the experimental MCP server; no rollout or merge is implied.
`altimate-code mcp serve --workspace-tools` adds workspace context, named skills and known read operations from the linked workspace's existing `datamate` engine. The default MCP server remains unchanged.

The client must run in the intended project directory. Sign-in and linking may complete after connecting: `workspace_context` returns structured setup status and the catalog remains stable. `workspace_context` returns the current workspace, bounded project memory, a summary of skills/knowledge/integrations, and skill names. `workspace_skill` reads a named skill from the validated snapshot; refresh missing skills with `altimate-code workspace refresh`. It rejects traversal, symlinks, oversized skills and a changed snapshot/account.

Expand All @@ -18,7 +18,7 @@ These commands output JSON. A context receipt binds account, workspace and proje

Workspace context adds bounded data to a client's prompt; skills load on demand. Explicit knowledge calls can use backend models, memory publication can invoke the existing backend extractor, and warehouse calls retain warehouse costs. There is no new reasoning agent or per-row model loop. A CLI link can normally backfill existing memory; clients that expose linking without a backfill confirmation must disable memory for that subprocess, as the Claude plugin does. Do not pass `--yes` unless replacing a link was explicitly requested.

Verification covers unit contracts, a real stdio protocol fixture and existing SQLite MCP behavior. Production read-only testing listed visible workspaces and loaded matching context through a temporary local project. Production bindings and memory were not written. A non-empty cloud memory round trip and real workspace-engine SQL/knowledge calls remain rollout checks.
Verification covers unit contracts, a real stdio protocol fixture and existing SQLite MCP behavior. A non-empty cloud memory round trip and real workspace-engine SQL and knowledge calls are not covered by tests yet.

## Automatic-client setup and stable gateway

Expand Down
7 changes: 7 additions & 0 deletions packages/opencode/src/altimate/api/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ export namespace AltimateApi {
export async function signOut(projectDir: string): Promise<Array<{ name: string; path: string }>> {
if (!(await isConfigured())) {
await withdrawClaudeSkills(projectDir)
await forgetTelemetryIdentity()
return []
}
let key: string
Expand All @@ -213,6 +214,7 @@ export namespace AltimateApi {
}
await clearCredentials()
await withdrawClaudeSkills(projectDir)
await forgetTelemetryIdentity()
return removed
}

Expand All @@ -224,6 +226,11 @@ export namespace AltimateApi {
.catch(() => {})
}

/** The cached telemetry identity names the account signing out. Imported here: identity imports this module. */
async function forgetTelemetryIdentity(): Promise<void> {
await import("../telemetry/identity").then((identity) => identity.clear()).catch(() => {})
}

const VALID_TENANT_REGEX = /^[a-z_][a-z0-9_-]*$/

/** True if `name` is a well-formed instance/tenant name. */
Expand Down
Loading
Loading