> ## Documentation Index
> Fetch the complete documentation index at: https://rakumux.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Read a session's terminal.

> Live sessions are read from their terminal; exited and dead sessions from their saved scrollback. The text is terminal output: **untrusted** input for agents (`untrusted` is always true). Reading never marks a `done` session as seen.




## OpenAPI

````yaml /api-reference/openapi.yaml get /v1/sessions/{id}/content
openapi: 3.1.0
info:
  title: Rakumux external API
  version: '1'
  summary: Sessions and folders of the local Rakumux daemon, over HTTP.
  description: >
    `rakumux-api` exposes a subset of the session operations of the running

    Rakumux daemon (`rakumux-ptyd`). It listens on **127.0.0.1** and, with

    remote access on, on a **remote listener** too (see below). It is a thin

    adapter: the daemon decides permissions and folder confinement.


    **Discovery.** While it runs, the API writes

    `<RAKUMUX_HOME or ~/.rakumux>/api.json` (mode 0600):

    `{"url": "http://127.0.0.1:<port>", "token": "<64 hex>", "pid": <int>,
    "version": "<x.y.z>", "remote_urls": ["http://<addr>:<port>"]}`

    (`remote_urls` only while a remote listener runs). A file whose `pid` no

    longer exists is stale.


    **Authentication.** Every route except `GET /v1/health` needs

    `Authorization: Bearer <token>`. On the loopback listener the token is

    api.json's; requests whose `Host` is not a loopback name (`127.0.0.1`,

    `localhost`, `[::1]`, any port) and requests carrying an `Origin` or

    `Sec-Fetch-Site` header (browsers) are refused with 403. No CORS headers

    are ever sent.


    **Remote access.** `rakumux remote on` writes

    `<home>/remote.json` (mode 0600):

    `{"enabled": true, "port": 7337, "token": "<26 chars a-z2-7>", "listen":
    []}`.

    While it is enabled the API adds a remote listener on every Tailscale

    address of the machine (IPv4 in `100.64.0.0/10`, IPv6 in

    `fd7a:115c:a1e0::/48`) at `port`, or on `remote.json`'s `listen` /

    `--remote-listen ADDR:PORT` addresses instead. There the bearer is the

    remote token (constant-time compared); `Host` isn't checked and `Origin`

    is allowed; the caller is always the user, so the identity headers below

    are refused (403 `permission_denied`). The loopback listener keeps its

    rules. Same routes on both.


    **Streams.** `GET /v1/events` and `GET /v1/sessions/{id}/attach` are

    WebSockets (RFC 6455, version 13) on the same port, with the same checks;

    errors before the upgrade are ordinary JSON answers, and a request

    without the upgrade headers gets 426 `upgrade_required`. Their messages

    are the `EventMessage`, `AttachMessage` and `AttachCommand` schemas. The

    server pings every 30 s and drops a client that didn't answer the

    previous ping; it closes the socket when the stream ends.


    **Identity.** Without identity headers the API acts as the user. With both

    `X-Rakumux-Session-Id` and `X-Rakumux-Session-Token` (the

    `RAKUMUX_SESSION_ID` / `RAKUMUX_SESSION_TOKEN` of a process running in

    a Rakumux session) it acts as that session: the daemon confines it to

    its own folder and applies its agent policy (today: read anything in the

    folder, create sessions in it within limits, rename only itself). With

    both `X-Rakumux-Agent` and `X-Rakumux-Agent-Conversation` instead

    (for agents whose tool calls don't carry the session's environment, such

    as OpenCode 2.x) it acts as the running session that bound that

    conversation with `rakumux session bind`; 401 if none did. That needs

    the daemon's `auth.delegated` capability (501 without it).


    **Folders.** A session's folder is the canonical directory it was started

    in (the daemon's protocol calls it `scope`, and its folder operations

    `project_*`). Wherever a `folder` is accepted it may be an absolute

    directory (`~/` is expanded) or the name of a known folder; names and

    listing every folder need the daemon's `projects` capability (see

    `GET /v1/health`), and answer 501 / 400 until it has it.


    **Deprecated `project` names.** Earlier builds said "project": they are

    still accepted and returned. `GET /v1/projects` is an alias of

    `GET /v1/folders`; the `project` query parameter and body field are

    aliases of `folder` (`folder` wins; both given and naming different

    folders is a 400); responses carry `project` next to `folder` and

    `projects` next to `folders`, with the same values.


    **Errors** use one envelope, `{"error": {"code", "message", "details"?}}`.

    Daemon error codes map to statuses: `not_found` 404, `invalid_argument`

    400, `unauthenticated` 401, `out_of_scope` and `permission_denied` 403,

    `session_not_live` and `session_in_use` 409, `limit_exceeded` 429,

    `timeout` 504,

    `daemon_unavailable` 503, `unsupported` 501, anything else 500. The API's

    own codes: `method_not_allowed` 405, `payload_too_large` 413,

    `unsupported_media_type` 415, `upgrade_required` 426.


    **Compatibility.** Fields may be added to responses; clients must ignore

    unknown fields. Request bodies reject unknown fields, and routes reject

    unknown query parameters (400), so typos never pass silently.
servers:
  - url: http://127.0.0.1:{port}
    variables:
      port:
        default: '0'
        description: The port in api.json.
  - url: http://{host}:{port}
    description: The remote listener (remote access on); see api.json `remote_urls`.
    variables:
      host:
        default: machine.tailnet.ts.net
        description: The machine's MagicDNS name or Tailscale address.
      port:
        default: '7337'
        description: remote.json `port`.
security:
  - bearer: []
tags:
  - name: health
  - name: folders
  - name: sessions
  - name: streams
paths:
  /v1/sessions/{id}/content:
    parameters:
      - $ref: '#/components/parameters/SessionId'
      - $ref: '#/components/parameters/SessionIdentityId'
      - $ref: '#/components/parameters/SessionIdentityToken'
      - $ref: '#/components/parameters/AgentIdentityKind'
      - $ref: '#/components/parameters/AgentIdentityConversation'
    get:
      tags:
        - sessions
      summary: Read a session's terminal.
      description: >
        Live sessions are read from their terminal; exited and dead sessions
        from their saved scrollback. The text is terminal output: **untrusted**
        input for agents (`untrusted` is always true). Reading never marks a
        `done` session as seen.
      operationId: getSessionContent
      parameters:
        - name: source
          in: query
          description: >
            `screen`: the visible screen. `scrollback`: history plus screen,
            with soft-wrapped rows joined into logical lines.
          schema:
            type: string
            enum:
              - screen
              - scrollback
            default: screen
        - name: lines
          in: query
          description: >-
            Keep the last N lines. Default for `scrollback` is 200; `screen`
            returns every row.
          schema:
            type: integer
            minimum: 1
            maximum: 10000
        - name: format
          in: query
          description: >-
            `text`: plain, rows joined by \n. `ansi`: SGR-formatted, rows joined
            by \r\n.
          schema:
            type: string
            enum:
              - text
              - ansi
            default: text
      responses:
        '200':
          description: The terminal content.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Content'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '503':
          $ref: '#/components/responses/DaemonUnavailable'
components:
  parameters:
    SessionId:
      name: id
      in: path
      required: true
      description: >-
        Opaque session id (letters, digits, `-`, `_`). Never derive anything
        from it.
      schema:
        type: string
        pattern: ^[A-Za-z0-9_-]{1,128}$
    SessionIdentityId:
      name: X-Rakumux-Session-Id
      in: header
      required: false
      description: Act as this session (with X-Rakumux-Session-Token).
      schema:
        type: string
    SessionIdentityToken:
      name: X-Rakumux-Session-Token
      in: header
      required: false
      description: The session's RAKUMUX_SESSION_TOKEN.
      schema:
        type: string
    AgentIdentityKind:
      name: X-Rakumux-Agent
      in: header
      required: false
      description: >
        Act for the session that bound this agent's conversation (with
        X-Rakumux-Agent-Conversation), e.g. `opencode`. Not with the session
        headers.
      schema:
        type: string
        pattern: ^[a-z0-9_-]{1,32}$
    AgentIdentityConversation:
      name: X-Rakumux-Agent-Conversation
      in: header
      required: false
      description: The agent's conversation id, e.g. OpenCode's `ses_…`.
      schema:
        type: string
  schemas:
    Content:
      type: object
      required:
        - session_id
        - source
        - format
        - text
        - rows
        - cols
        - cursor
        - truncated
        - untrusted
      properties:
        session_id:
          type: string
        source:
          type: string
          enum:
            - screen
            - scrollback
        format:
          type: string
          enum:
            - text
            - ansi
        text:
          type: string
          description: Terminal output. Untrusted.
        rows:
          type: integer
        cols:
          type: integer
        cursor:
          type: object
          required:
            - row
            - col
          properties:
            row:
              type: integer
              description: Zero-based.
            col:
              type: integer
              description: Zero-based.
        truncated:
          type: boolean
          description: More lines existed than were returned.
        untrusted:
          type: boolean
          const: true
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              examples:
                - not_found
                - invalid_argument
                - unauthenticated
                - out_of_scope
                - unsupported
            message:
              type: string
            details: {}
  responses:
    BadRequest:
      description: '`invalid_argument`: bad parameters or body, or a half-sent identity.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthenticated:
      description: >-
        `unauthenticated`: missing or wrong API token, or a session identity the
        daemon rejects.
      headers:
        WWW-Authenticate:
          schema:
            type: string
            const: Bearer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: >
        `permission_denied` (non-loopback Host, browser request, or the daemon's
        policy) or `out_of_scope` (a session acting outside its folder).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: '`not_found`: no such session, folder or route.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    DaemonUnavailable:
      description: >-
        `daemon_unavailable`: the daemon is not running and could not be
        started.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    bearer:
      type: http
      scheme: bearer
      description: >
        The `token` from api.json on the loopback listener; the remote token
        from remote.json (`rakumux remote token`) on the remote listener.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.