Skip to content
Merged
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
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,17 @@ Move a repository or session onto the right shared memory:
```bash
tap use @acme/engineering
tap use @acme/engineering --flight @acme/+release-42
tap info
tap url 42 --open
```

`tap info` shows the hub you are signed in to, the default keg, the active
flight, and the kegs that flight covers. `tap url [REF]` prints the hub web
link for the current keg, a node (`42`, `@acme/engineering/42`), a flight
(`+release-42`), or a namespace (`@acme`); `--open` also opens it in the
browser. Agents get the same link from the MCP `url` tool. `tap explore kegs|flights|people QUERY`
searches the hub's public directory; your own kegs come from `tap keg list`.

Export a keg for backup or migration:

```bash
Expand Down
12 changes: 11 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,17 @@ writing KEG files directly.
succeeding. A keg's version never changes after creation. Keg listings,
`tap keg info`, and orientation label KEGv2 kegs, and an operation a keg's
version does not offer fails with `KEG_VERSION_UNSUPPORTED`.
- `tap keg list` - list kegs visible on a hub.
- `tap info` - show the hub and signed-in user, the default keg, the active
flight, and the kegs that flight covers.
- `tap url [REF]` - print the hub web link for the current keg, a node (`42`,
`@namespace/keg/42`, `keg:~alias/42`), a keg, a flight (`+slug`), or a
namespace (`@namespace`). `--open` also opens it in the browser and `--json`
adds the kind and canonical reference. MCP's `url` tool returns the same
link.
- `tap keg list` - list your kegs on a hub. The list is complete; search is
only for the public directory.
- `tap explore kegs|flights|people QUERY` - search the hub's public directory.
A match grants no access.
- `tap keg grant|grants|revoke` - manage per-keg access.
- `tap keg visibility public|private` - set keg visibility.
- `tap keg rename @namespace/old new` - rename a keg alias in its namespace.
Expand Down
5 changes: 3 additions & 2 deletions docs/ai-coding-agents/agent-conventions.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,9 @@ Tapper recognizes two link forms in node bodies:
- **Intra-keg:** `[title](../NODEID)` — relative path from the
current node's directory to the target node's directory.
- **Cross-keg:** `keg:@namespace/keg/NODEID` — fully qualified reference that
the index parses into a cross-keg edge. Bare names can also be used when the
active config resolves them unambiguously.
the index parses into a cross-keg edge; on a Hub a flight's cover follows it.
`keg:@namespace/keg/0` links a KEG's root. The `@` is required. Bare names can
also be used when the active config resolves them unambiguously.

Both forms appear in backlinks. Prefer intra-keg links when the target
is in the same keg — they resolve in any markdown renderer.
Expand Down
26 changes: 18 additions & 8 deletions docs/ai-coding-agents/mcp-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ With a default keg:

Tool names are `<resource>_<verb>` (`node_read`, `flight_list`,
`agent_edit`), and each matches the CLI command `tap <resource> <verb>`.
`orient` and `url` are single words, matching `tap orient` and `tap url`.

The MCP server exposes a shared agent surface rather than every machine-local
CLI capability. Exact tool availability follows the installed Tapper version
Expand Down Expand Up @@ -105,6 +106,7 @@ explicitly configured root failed to initialize.
| `keg_info` | Show concise diagnostics for the resolved keg |
| `keg_settings_read` | Read targeted minimal config for one or more selected KEGs; `minimal=false` reads one complete config |
| `node_stats` | Show node statistics |
| `url` | Hub web link for a KEG, note, flight, or namespace, to hand the reader a clickable link |

### Write

Expand Down Expand Up @@ -192,14 +194,22 @@ vocabulary to ask.

### Discovery And Identity

| Tool | Description |
| --- | --- |
| `keg_list` | List canonical KEGs, effective roles, and winning granting flights for the live pinned-root graph by default or exactly one supplied `flight` |
| `keg_search` | Search all identity-accessible KEG refs, titles, and summaries, including KEGs outside the flight graph; results grant no operational access |
| `session_info` | Return structured credential-safe `identities[]` and exact pinned-root-context `kegs[]` |
| `namespace_list` | List the namespaces you belong to, with your role |
| `namespace_search` | Search the people and org namespaces visible on the Hub; discovery only |
| `invitation_list` | List pending invitations to namespaces and kegs addressed to you; read-only, since accepting is the user's decision |
Every tool belongs to one category, and its description opens with the
category name: **Session** (identity and what the session may do; always
available), **Memory** (reading and writing notes in the covered KEGs),
**Explore** (the Hub's public directory), or **Manage** (KEGs, settings,
schemas, repair, flights, and agents). Your own resources are listed, never
searched; search is for note content (`node_search`) and the public directory.

| Tool | Category | Description |
| --- | --- | --- |
| `keg_list` | Memory | List canonical KEGs with effective roles, titles, descriptions, and winning granting flights for the live pinned-root graph by default or exactly one supplied `flight` |
| `session_info` | Session | Return structured credential-safe `identities[]` and exact pinned-root-context `kegs[]` |
| `namespace_list` | Session | List the namespaces you belong to, with your role |
| `invitation_list` | Session | List pending invitations to namespaces and kegs addressed to you; read-only, since accepting is the user's decision |
| `explore_kegs` | Explore | Search the Hub's public KEGs; results grant no operational access |
| `explore_flights` | Explore | Search the Hub's public flights; metadata only |
| `explore_people` | Explore | Search the people and org namespaces on the Hub; discovery only |

Each identity includes only its hub locator, user ID, username, display name,
default namespace, and namespace names. Tokens, email, scopes, cookies, expiry,
Expand Down
14 changes: 7 additions & 7 deletions docs/ai-coding-agents/orient.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ instructions: |
Snapshot any node before changing public API guidance.
```

`summary` is the concise discovery description shown by graph orientation and
identity `keg_search`.
`summary` is the concise discovery description shown by graph orientation,
`keg_list`, and, for a public KEG, `explore_kegs`.
It should help an agent decide whether the KEG is relevant and is not
automatically truncated. `instructions` is targeted operational guidance.
Aggregate orientation never includes it.
Expand Down Expand Up @@ -117,11 +117,11 @@ access requires reconnecting. A failed refresh returns
`SESSION_REFRESH_FAILED`, keeps the prior mode and tool surface, and reports
`toolsChanged:false`.

`keg_search` is deliberately outside this authority flow. It performs a
case-insensitive literal match over canonical ref, title, and summary for all
identity-accessible KEGs and returns at most 50 canonical rows. Finding a KEG
does not change authority: no-flight calls still use the identity role, while a
real-flight call must also cover it.
`explore_kegs`, `explore_flights`, and `explore_people` are deliberately
outside this authority flow. They search only the Hub's public directory with a
literal, non-empty query and return a capped page. Finding a KEG does not change
authority: a real-flight call must still cover it. Your own KEGs come from
`keg_list`, which is complete and needs no query.

## MCP Resource

Expand Down
10 changes: 5 additions & 5 deletions docs/api-compatibility.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
# REST API contract compatibility

Tapper and Tapper Hub select a REST contract independently of their release
numbers. The sole supported revision is `2026-09-28`, which replaced `2026-09-11` when grep, listing, and relationship responses gained server-side projection.
numbers. The sole supported revision is `2026-10-12`, which replaced `2026-09-28` when `POST /api/v1/namespaces/search` was removed in favor of `POST /api/v1/explore`, the public directory of KEGs, flights, and people. `2026-09-28` had replaced `2026-09-11` when grep, listing, and relationship responses gained server-side projection.
Change the revision for incompatible API behavior. Compatible additions and
fixes retain the revision; release numbers remain diagnostic metadata.

`GET /api/version` is public, requires no database access or credentials, and
returns `Cache-Control: no-store` with the actual server build version:

```json
{"server_version":"0.24.0","api_versions":["2026-09-28"]}
{"server_version":"0.24.0","api_versions":["2026-10-12"]}
```

Every request under `/api/v1`, including event-stream handshakes and telemetry,
must send exactly one `Tapper-API-Version: 2026-09-28` header. Accepted requests
must send exactly one `Tapper-API-Version: 2026-10-12` header. Accepted requests
echo it in the response. Clients send their release in `Tapper-Client-Version`.
The Hub validates the contract before authentication, flight authority, or
application handlers. Discovery, health, OAuth, browser routes, and hosted
Expand All @@ -26,7 +26,7 @@ duplicate, comma-joined, or unsupported values return HTTP 400 with
`operationPerformed: false`, for example:

```json
{"error":"unsupported REST contract; upgrade Tapper and Hub together","code":"API_VERSION_UNSUPPORTED","requested_api_versions":["2099-01-01"],"supported_api_versions":["2026-09-28"],"server_version":"0.24.0","operationPerformed":false}
{"error":"unsupported REST contract; upgrade Tapper and Hub together","code":"API_VERSION_UNSUPPORTED","requested_api_versions":["2099-01-01"],"supported_api_versions":["2026-10-12"],"server_version":"0.24.0","operationPerformed":false}
```

CLI invocations and MCP connections discover each selected Hub once, before
Expand Down Expand Up @@ -57,7 +57,7 @@ working against the new Hub, and new clients reject legacy Hubs. Update custom
scripts explicitly, for example:

```sh
curl -H 'Tapper-API-Version: 2026-09-28' \
curl -H 'Tapper-API-Version: 2026-10-12' \
-H "Authorization: Bearer ${TAPPER_TOKEN}" \
https://hub.example.com/api/v1/whoami
```
Expand Down
26 changes: 26 additions & 0 deletions docs/architecture/configuration-resolution.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,32 @@ KEG settings are separate from Tapper user/project config — different schema,
different purpose (KEG metadata vs resolver settings) — and are read or written
through the Hub.

## Hub Web Links

`tap url` and the MCP `url` tool (`Tap.WebURL`, `pkg/tapper/tap_url.go`) turn a
reference into a link to the Hub's web pages. Resolution reuses the keg
resolution above: a keg or node reference resolves to its target, whose
configured hub URL is the link's origin. Flights and namespaces use the
selected hub. Only a keg-local `keg:~alias` reads the current keg's settings,
so the command skips the startup credential refresh. A Hub serving its own MCP
sets `Tap.WebBaseURL` to its public address and resolves kegs through its
injected `KegResolver`.

The links depend on four Hub browser routes. They are outside the Hub's REST
contract (see [API Compatibility](../api-compatibility.md)), so a Hub that
moves them breaks these links:

| Page | Path |
| --- | --- |
| Namespace | `/@namespace` |
| KEG | `/@namespace/keg` |
| Note | `/@namespace/keg/42` (plain decimal node id, no code) |
| Flight | `/@namespace/+slug` |

Every namespace, keg, and slug segment matches `^[a-z0-9_-]+$`, so paths are
never escaped. A raw HTTP(S) keg endpoint that no configured hub serves has no
web page and is refused.

## Read Next

- [Resolution Order](../configuration/resolution-order.md)
Expand Down
2 changes: 1 addition & 1 deletion docs/configuration/flights.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ on the agent's page on Hub.
- Recovery-only mode applies only when an explicitly configured root is
missing, inaccessible, invalid, or unavailable. Seeing only `orient`,
`session_refresh`, `flight_list`, `flight_read`, `session_info`, and
`keg_search` means configured authority failed to initialize. Repair the
`guide` means configured authority failed to initialize. Repair the
configured root outside MCP, then call `session_refresh` and `orient`.
- Every MCP connection pins either no-flight authority or one real root at
initialization. Configuration and account-preference changes cannot change
Expand Down
14 changes: 8 additions & 6 deletions docs/development/gotchas.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,17 @@ Behavior that has surprised people before.
KEG metadata uses `description`; legacy settings/archive `summary` is accepted
only when description is absent. Flight descriptions flow through manifests,
REST/MCP, editing, and hashes independently of instructions and authority.
`orient` renders full active instructions, the complete active effective KEG cover; child flights require explicit inspection. No-flight orientation is search-first. `flight_search` returns
at most 50 ordered metadata rows with a truncation notice; `guide` serves detailed
`orient` renders full active instructions, the complete active effective KEG cover; child flights require explicit inspection. No-flight orientation points to the complete `keg_list` and `flight_list`, and to the `explore_*` tools for the public directory, which return a capped page with a truncation notice; `guide` serves detailed
canonical guidance by topic. Display descriptions never affect authority revisions.

The coordinated REST/MCP replacement is in progress, not complete. Cover entries
now carry depth (default 2, range 1–8). The Hub composes authority through declared
same-Hub settings relationships. Flights carry no capabilities; an agent's tools decide what a session may do.
`keg:~alias/node` explicitly resolves the source KEG's settings alias; canonical
references keep their meaning. Orientation request transport requires a root and
now carry depth (default 2, range 1–8). The Hub composes a flight's cover from
its listed KEGs and the same-Hub KEGs their notes link to; a cover entry with
role `none` blocks a KEG however it is reached. Flights carry no capabilities;
an agent's tools decide what a session may do. `keg:~alias/node` explicitly
resolves the source KEG's settings alias (settings `aliases`, formerly `links`,
which is still read when `aliases` is absent); canonical references keep their
meaning. Orientation request transport requires a root and
active flight, with no revision acknowledgment requirement. The hub publishes
its own verification status for the replacement's scope and gaps.

Expand Down
7 changes: 4 additions & 3 deletions integrations/content/agent-orient.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ flight on subsequent operations. keg selects a target and never grants access.

orient returns current active instructions, effective readable KEGs, and
immediate readable children. Orient with a child reference to inspect the next
level. With no root, orientation is search-first; keg_search and flight_search
find identity-readable metadata. Discovery is independent of selection.
level. With no root, keg_list and flight_list are the complete lists of what
your identity reads, and explore_kegs and explore_flights search the public
directory. Discovery is independent of selection.
flight_read inspects a permission-filtered declared manifest and its hash;
it never activates a session or selects operational authority.

Expand All @@ -25,7 +26,7 @@ MCP and starts a fresh connection. session_refresh only retries failed initial
activation; it cannot change an active connection.

A failed configured root exposes recovery tools: orient, session_refresh,
flight_list, flight_read, session_info, keg_search, flight_search, guide. Repair
flight_list, flight_read, session_info, guide. Repair
the configured root outside MCP, then retry session_refresh and orient.
An active flight can have no effective KEGs and still expose the full inventory;
KEG denials in that scope do not imply missing tools.
Expand Down
14 changes: 11 additions & 3 deletions integrations/content/linking.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,19 @@ Tapper supports these link forms in node bodies:
- **Cross-keg (same namespace):** `[title](keg:ALIAS/NODEID)` — names a KEG
in the source namespace.
- **Settings alias:** `[title](keg:~ALIAS/NODEID)` — explicitly resolves an
alias declared in the source KEG settings. The tilde distinguishes a settings
alias from a KEG name.
alias declared in the source KEG settings `aliases`. The tilde distinguishes
a settings alias from a KEG name. An alias keeps links working when the KEG
it names moves: only the alias's target changes.
- **Cross-keg (fully qualified):**
`[title](keg:@NAMESPACE/ALIAS/NODEID)` — identifies the namespace and keg
explicitly and is parsed into a cross-keg edge.
explicitly and is parsed into a cross-keg edge. `NODEID` may be `0`, the
KEG's root node, to link the KEG as a whole. The `@` is required:
`keg:NAMESPACE/ALIAS/NODEID` is not a link, and `keg_check` reports it.

On a Hub, a flight that covers a KEG also covers the KEGs its notes link to,
as far as the cover entry's depth reaches, so a cross-keg link can widen what
an agent under that flight may read. A flight blocks a KEG by listing it with
role `none`.

Indexed links appear in backlinks when readable. Prefer intra-keg links when the target is in
the same keg. A bare `keg:` reference in node prose is plain text: it does not
Expand Down
22 changes: 15 additions & 7 deletions integrations/content/snapshot-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,19 @@
are cheap, deduplicated by content hash, and stored inside the node they
belong to.

**Important: snapshots are stored inside the node. Removing a node
removes its snapshot history with it. A snapshot does not protect
against `mcp__tapper__node_delete`.** Before a removal, copy the content
somewhere that survives the deletion — read it with `mcp__tapper__node_read`
and keep the result in your working context, or write it to another
node first. If you are not certain the removal is correct, defer it.
**Important: snapshots are stored inside the node. A snapshot does not
protect against `mcp__tapper__node_delete`.** On a hub keg, a removed node
goes to the keg's trash with its snapshots, files, and images:
`mcp__tapper__trash_list` shows it and `mcp__tapper__trash_restore` puts it
back, until the keg's retention purges it. Until then its id stays reserved:
no node can be moved onto it. An archive restore that needs the id moves the
trashed node to a new id first (`displaced_from` in `mcp__tapper__trash_list`
names the old one), and restoring it puts it back at the new id. Elsewhere, removing a node removes
its snapshot history with it. Either way, before a removal you are not sure
of, copy the content somewhere that survives the deletion — read it with
`mcp__tapper__node_read` and keep the result in your working context, or
write it to another node first. If you are not certain the removal is
correct, defer it.

Snapshots do protect in-place edits. Take one before any of:

Expand Down Expand Up @@ -41,4 +48,5 @@ Recover with:

If you are unsure whether an in-place edit warrants a snapshot, take
one. The cost is negligible. For `node_delete`, a snapshot is not a
recovery path — preserve the content some other way first.
recovery path — the trash is, where the keg has one; otherwise preserve the
content some other way first.
Loading
Loading