Skip to content

docs: document session sharing for 1.x (#562) - #657

Merged
Rachael-Graham merged 2 commits into
kagent-dev:mainfrom
boxcee-interview:fix/issue-562
Nov 3, 2026
Merged

Rachael-Graham merged 2 commits into
kagent-dev:mainfrom
boxcee-interview:fix/issue-562

Conversation

@boxcee-interview

Copy link
Copy Markdown
Contributor

Closes #562

Session sharing had no page in the 1.x docs, while the ttl field is referenced from "Controller identity" and "Operational considerations" on the assumption that one exists. This adds 1.x/agents/share-a-session.md (weight 60, after Human in the loop) covering the surface from the issue:

  • the capability model: a share is a token that acts with the owner's access, not a grant to a named user
  • READ_ONLY presented as a scope, with the insecure-mode caveat that it is not a security boundary in a default install
  • ttl, expiresAt, and the config.sessions.shareMaxTtl cap, including rejection of a ttl above the cap
  • spending a share via X-Share-Token, and revoking via RevokeSessionShare
  • no CLI verb: gRPC and UI only

The cap is linked to the existing pages rather than restated.

How to Test

# in docs-site/
hugo mod get ./...
hugo --config hugo.yaml,hugo.preview.yaml -D --gc --minify
# -> HUGO_EXIT=0, no new ERROR/WARN lines (the single WARN is the pre-existing
#    `.Site.Data was deprecated` notice)
ls public/kagent/1.x/agents/share-a-session/index.html
# -> page renders; glossary term "Session" resolves; both linked anchors
#    (operational-considerations#expire-idle-sessions,
#     about/substrate-runtime/identity#controller-authentication-modes) exist

Signed-off-by: Moritz Schmitz von Hülst <mschmitzvonhuelst@gmail.com>
@boxcee-interview

Copy link
Copy Markdown
Contributor Author

The deploy check failed in the Preview workflow: wrangler aborted with 'In a non-interactive environment, it's necessary to set a CLOUDFLARE_API_TOKEN environment variable' (job 122985167728). The change itself is a single docs markdown file, so the content/build checks all passed — this looks like a missing repo secret rather than something in the diff. A re-run (or restoring the secret) should clear it; no code change from me here.

@Rachael-Graham Rachael-Graham left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thank you for this. The capability framing and the insecure-mode warning are exactly the register the issue asked for, and linking the cap rather than restating it is right. Since you opened this, kagent-dev/kagent#3453 landed for rc1 and changed the share model, so several claims below no longer match the code; please re-verify the page against v1.0.0-rc1.

Comment on lines +14 to +19
Each share carries one of two permissions:

| Permission | Allows |
| ---------- | ------ |
| `READ_ONLY` | A2A `get`, `list`, and `subscribe`. |
| `READ_WRITE` | The `READ_ONLY` operations, plus `send` and `cancel`. |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Blocking. At rc1 READ_WRITE and the permission field are gone, and every share is read-only (SessionShare in proto/kagent/api/v1/sessions.proto, CreateShare in go/core/internal/service/session/service.go). Drop the table and state that a share grants A2A get, list, and subscribe only.


## A share is a capability, not a grant to a user

The token resolves to the conversation's owner, and the backend answers as though the owner had asked, keeping the caller's own identity only for the record. Anyone holding the token acts with the owner's access to that one Session. The share does not name a person, and it does not create a second principal.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Blocking. At rc1 the recipient's own identity is checked against policy, and the owner supplies only the storage lookup (authorize in go/core/internal/service/session/service.go: "Checking the owner would lend their grants to visitors"). "Acts with the owner's access" is no longer true, so please rewrite this section to say the holder needs their own read permission on the Session.


A caller spends the share by presenting the token in the `X-Share-Token` header. The controller resolves the token to the owner and scopes the call to that Session.

The owner revokes a share with `RevokeSessionShare`, keyed by the `SessionShare.id`. Revoking withdraws the holder's access immediately; the share record remains so the owner can confirm it is gone from `ListSessionShares`. Deleting the Session removes its shares with it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Blocking. RevokeSessionShare runs DELETE FROM session_share (go/core/internal/database/shares.go), so the record does not remain and does not appear in ListSessionShares afterward. Please say that revoking deletes the share.


When you create a share you can set `CreateSessionShareRequest.ttl` to a positive lifetime, counted from creation. The returned `SessionShare.expiresAt` records when the token stops granting access; once that time passes, the token lookup returns not found, and the owner still sees the expired share in `ListSessionShares` and can revoke it.

Omit `ttl` and `expiresAt` stays unset. The share then grants access until it is revoked or the Session is deleted.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should fix. This line says an omitted ttl leaves expiresAt unset, while line 38 says a positive cap supplies the lifetime for an omitted ttl. Line 38 matches the code, so this line needs "when no cap is set".

| `READ_WRITE` | The `READ_ONLY` operations, plus `send` and `cancel`. |

> [!WARNING]
> **`READ_ONLY` is not a security boundary in a default install.** `config.server.auth.mode` defaults to `insecure`, where a caller supplies its own identity in `X-User-Id`. A caller can claim the owner's identity and reach the Session without any share token at all. Treat `READ_ONLY` as a scope on what the share link can do, not as access control: the boundary holds only under `trusted-proxy`, with a credential-validating proxy in front of the controller. For the modes themselves, see [Controller authentication modes]({{< link path="about/substrate-runtime/identity#controller-authentication-modes" >}}).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should fix. This warning repeats the owner's-access claim from line 12. Re-check this sentence once that is settled, because the insecure-mode impersonation point may still hold on its own.

@@ -0,0 +1,51 @@
---

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should fix. The issue scoped this as a UI procedure, and the page has no steps. Either add the UI steps or retitle the page as a concept page ("Session sharing"), because "Share a session" promises a task.

author: kagent.dev
---

A share lets the owner of a {{< gloss "Session" >}}Session{{< /gloss >}} give another account access to that one conversation. Creating a share produces a token; a caller presenting that token reaches the shared Session without becoming its creator. kagent exposes three RPCs for this: `CreateSessionShare`, `ListSessionShares`, and `RevokeSessionShare`. There is no `kagent` CLI verb for sharing — it is a gRPC and UI procedure, not something you script from the command line.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit. The em dash joins two clauses. Split them into two sentences.

Signed-off-by: Moritz Schmitz von Hülst <mschmitzvonhuelst@gmail.com>
@boxcee-interview

Copy link
Copy Markdown
Contributor Author

Re-verified the page against v1.0.0-rc1 (kagent-dev/kagent) and rewrote it per the review:

  • Dropped the READ_ONLY/READ_WRITE table. Every share is now read-only (A2A get/list/subscribe only); no send/cancel.
  • Rewrote the capability section: the controller checks the holder's own identity against policy and uses the share only for the storage lookup, so the holder needs their own read permission on the Session.
  • ttl section now says the cap supplies the lifetime for an omitted ttl only when a cap is set.
  • Revoking now states the share is deleted (not retained) and drops out of ListSessionShares.
  • Split the em-dash sentence, and retitled the page to a concept page ('Session sharing') since the issue scoped it as a concept without a task.
    Hugo build passes clean.

@Rachael-Graham Rachael-Graham left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

TY very much! Merging.

@Rachael-Graham
Rachael-Graham merged commit 918ec45 into kagent-dev:main Nov 3, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Document session sharing

2 participants