Skip to content

Latest commit

 

History

History
491 lines (375 loc) · 11.1 KB

File metadata and controls

491 lines (375 loc) · 11.1 KB

Pod API Design – Base Shape for App Host & Ecosystem Apps

This document defines the Pod API shape that the App Host (VS Code-based shell) and ecosystem apps will call via the PodApiClient (pods:// URIs).

This is not intended to be a fully stable public API yet. It is a v0 internal contract to:

  • Unblock the App Host and core apps (Mesh Explorer, Chat, etc.).
  • Provide a coherent set of endpoints and JSON shapes.
  • Stay aligned with security, context, and realm/pod isolation principles.

1. Goals

  • Provide a consistent, simple HTTP/JSON API for each pod.
  • Allow the App Host to talk to a pod via a small set of domains:
    • auth – sessions/tokens (later).
    • context – who/where we are.
    • apps – which apps are available on this pod (optional, later).
    • chat – channels, messages, threads.
    • mesh – realm/pod metadata, health.
    • (later) forum, media, gov, etc.
  • Map cleanly to the pods:// URI scheme used by the App Host:
    • e.g. pods://chat/listChannelsGET /api/chat/channels.

For now, the implementation can be in-memory/mocked and later wired into real data stores and services.

2. Base Conventions

2.1 HTTP & Base Path

  • All pod APIs are served under /api.
  • Versioning:
    • For v0, endpoints are at /api/....
    • When stable, we can adopt /api/v1/... while keeping v0 for internal use only.

2.2 Authentication

  • For this first iteration, it is acceptable to:
    • Stub auth checks,
    • Or use a simple header such as Authorization: Bearer <token> and ignore validation.
  • The API must be designed to expect auth in the future:
    • All handlers should be structured so that adding auth checks later does not break the shape.

2.3 Response Envelope

All endpoints should respond with a consistent JSON envelope:

{
  "ok": true,
  "error": null,
  "data": { /* domain-specific payload */ }
}

On failure:

{
  "ok": false,
  "error": {
    "code": "SOME_CODE",
    "message": "Human-readable error",
    "details": { /* optional */ }
  },
  "data": null
}

The App Host's PodApiClient expects this envelope.

2.4 Error Codes

Define a small initial set of error codes:

  • UNAUTHORIZED
  • FORBIDDEN
  • NOT_FOUND
  • VALIDATION_FAILED
  • INTERNAL_ERROR
  • NOT_IMPLEMENTED

This can expand later as needed.

3. Context & Identity Endpoints

These endpoints give the client basic information about where it is and who it is.

3.1 GET /api/context

Purpose: Return the active realm/pod/user context from this pod's perspective.

Mapped pods:// URI: pods://mesh/getContext

Request:

  • Method: GET
  • Path: /api/context
  • Auth: optional placeholder for now.

Response data shape:

{
  "realm": {
    "id": "slskdn-main-v1",
    "name": "Main Realm"
  },
  "pod": {
    "id": "pod-1234",
    "name": "My Pod",
    "roles": ["owner", "admin"]  // roles relative to this pod
  },
  "user": {
    "id": "user-abcdef",
    "displayName": "Alice",
    "roles": ["member"],          // roles relative to this pod
    "isF1000": false              // if applicable in this realm
  }
}

This endpoint allows the App Host to:

  • Initialize MeshContext (realmId/podId/userId).
  • Render status bar indicators and basic identity info.

4. Apps Listing (Optional, Future)

Later, pods may expose app manifests or capabilities.

4.1 GET /api/apps

Purpose: List apps/capabilities that this pod wants to expose to the client.

Mapped pods:// URI: pods://mesh/listApps (for future use).

Request:

  • GET /api/apps

Response data shape (initial idea):

{
  "apps": [
    {
      "appId": "mesh.chat",
      "enabled": true
    },
    {
      "appId": "mesh.mesh",
      "enabled": true
    }
  ]
}

For now, the App Host can rely on local manifests. This endpoint can be wired in later to allow pods to publish app availability and configuration.

5. Mesh Domain – Realm & Pod Metadata

These endpoints support the "Mesh Explorer" app.

5.1 GET /api/mesh/pods

Purpose: Return a list of pods known to this pod in its realm (and optionally cross-realm, if applicable).

Mapped pods:// URI: pods://mesh/listPods

Request:

  • GET /api/mesh/pods

Response data shape:

{
  "realm": {
    "id": "slskdn-main-v1",
    "name": "Main Realm"
  },
  "pods": [
    {
      "id": "pod-1234",
      "name": "My Pod",
      "status": "online",     // "online" | "offline" | "degraded" | "unknown"
      "healthScore": 0.98,    // optional float 0.0–1.0
      "roles": ["self"]       // e.g. ["self"], ["peer"], ["bridge"]
    },
    {
      "id": "pod-5678",
      "name": "Friend Pod",
      "status": "online",
      "healthScore": 0.87,
      "roles": ["peer"]
    }
  ]
}

For now, return a mock list. Later, plug into the real mesh topology / HealthManager.

5.2 GET /api/mesh/pods/:podId/health

Purpose: Detailed health info for a specific pod.

Mapped pods:// URI: pods://mesh/getPodHealth?podId=pod-1234

Request:

  • GET /api/mesh/pods/{podId}/health

Response data shape:

{
  "podId": "pod-1234",
  "status": "online",
  "healthScore": 0.98,
  "lastSeen": "2025-12-10T12:34:56Z",
  "metrics": {
    "latencyMs": 35,
    "failureRate": 0.01
  }
}

Initial implementation can be fully mocked.

6. Chat Domain – Channels & Messages

These endpoints back the Chat app surfaces.

6.1 Channel Data Model (v0)

Minimal shapes:

interface ChatChannel {
  id: string;
  name: string;
  isPrivate: boolean;
  topic?: string;
  createdAt: string;     // ISO timestamp
  createdBy: string;     // user id
}
interface ChatMessage {
  id: string;
  channelId: string;
  authorId: string;
  authorDisplayName: string;
  body: string;
  createdAt: string;     // ISO timestamp
  editedAt?: string;     // ISO timestamp
  threadRootId?: string; // if this message is part of a thread
}

6.2 GET /api/chat/channels

Purpose: List chat channels available to the current user in this pod.

Mapped pods:// URI: pods://chat/listChannels

Request:

  • GET /api/chat/channels

Response data shape:

{
  "channels": [
    {
      "id": "chan-general",
      "name": "#general",
      "isPrivate": false,
      "topic": "General discussion",
      "createdAt": "2025-12-10T12:00:00Z",
      "createdBy": "user-abcdef"
    },
    {
      "id": "chan-mesh",
      "name": "#mesh-dev",
      "isPrivate": true,
      "topic": "Mesh development",
      "createdAt": "2025-12-10T12:05:00Z",
      "createdBy": "user-abcdef"
    }
  ]
}

Initial implementation can return a static list.

6.3 GET /api/chat/channels/:channelId/messages

Purpose: Fetch messages in a given channel, with pagination.

Mapped pods:// URI: pods://chat/getChannelMessages?channelId=...

Request:

  • GET /api/chat/channels/{channelId}/messages
  • Query parameters:
    • before (optional): message ID or timestamp cursor.
    • limit (optional): integer, default 50.

Response data shape:

{
  "channel": {
    "id": "chan-general",
    "name": "#general"
  },
  "messages": [
    {
      "id": "msg-1",
      "channelId": "chan-general",
      "authorId": "user-abcdef",
      "authorDisplayName": "Alice",
      "body": "Hello world",
      "createdAt": "2025-12-10T12:10:00Z"
    },
    {
      "id": "msg-2",
      "channelId": "chan-general",
      "authorId": "user-ghijkl",
      "authorDisplayName": "Bob",
      "body": "Hi Alice",
      "createdAt": "2025-12-10T12:11:00Z"
    }
  ],
  "paging": {
    "hasMore": false,
    "nextCursor": null
  }
}

For v0, you can ignore pagination and just return a fixed array with hasMore: false.

6.4 POST /api/chat/messages

Purpose: Send a new message to a channel.

Mapped pods:// URI: pods://chat/sendMessage

Request:

  • POST /api/chat/messages
  • JSON body:
{
  "channelId": "chan-general",
  "body": "Message text",
  "threadRootId": "msg-1"  // optional, if replying in a thread
}

Response data shape:

{
  "message": {
    "id": "msg-3",
    "channelId": "chan-general",
    "authorId": "user-abcdef",
    "authorDisplayName": "Alice",
    "body": "Message text",
    "createdAt": "2025-12-10T12:12:00Z",
    "threadRootId": "msg-1"
  }
}

Initial implementation can:

  • Accept any body,
  • Echo back a message object with generated IDs and timestamps,
  • Store in an in-memory list per pod process (no persistence needed in v0).

6.5 POST /api/chat/channels

Purpose: Create a new channel.

Mapped pods:// URI: pods://chat/createChannel

Request:

  • POST /api/chat/channels
  • JSON body:
{
  "name": "#new-channel",
  "isPrivate": false,
  "topic": "Optional topic"
}

Response data shape:

{
  "channel": {
    "id": "chan-new-channel",
    "name": "#new-channel",
    "isPrivate": false,
    "topic": "Optional topic",
    "createdAt": "2025-12-10T12:20:00Z",
    "createdBy": "user-abcdef"
  }
}

For v0, you can:

  • Validate that name is non-empty.
  • Append to an in-memory channels list.

7. Mapping pods:// URIs to HTTP Endpoints

The App Host's PodApiClient operates with pods:// URIs. The mapping in v0 should be:

  • pods://mesh/getContextGET /api/context
  • pods://mesh/listPodsGET /api/mesh/pods
  • pods://mesh/getPodHealth?podId=XGET /api/mesh/pods/X/health
  • pods://chat/listChannelsGET /api/chat/channels
  • pods://chat/getChannelMessages?channelId=XGET /api/chat/channels/X/messages
  • pods://chat/sendMessagePOST /api/chat/messages
  • pods://chat/createChannelPOST /api/chat/channels

The exact query parameter encoding for pods:// URIs is up to the App Host implementation; the pod-side only needs the HTTP routes.

8. Security & Hardening Considerations

Even in v0/stub form, adhere to these principles:

  • No cross-realm impersonation:
    • A pod should only report data about its own realm/pod and known peers as configured.
  • Role awareness (future):
    • The API shapes include roles fields so later we can enforce:
      • Who can create channels,
      • Who can see private channels,
      • etc.
  • Error handling:
    • All errors must be mapped to the response envelope and not leak internal stack traces.

As the mesh governance and MCP layers are wired in, additional checks will be added on top of these endpoints to enforce content and behavior policies.

9. Initial Implementation Scope

For the first implementation pass, it is sufficient to:

  • Implement all endpoints with in-memory, process-local data:
    • GET /api/context → hard-coded context or simple config.
    • GET /api/mesh/pods → fixed mock list.
    • GET /api/mesh/pods/:podId/health → mock health payloads.
    • GET /api/chat/channels → in-memory list of channels.
    • POST /api/chat/channels → append to in-memory channels.
    • GET /api/chat/channels/:channelId/messages → in-memory messages per channel.
    • POST /api/chat/messages → append to in-memory messages.

This is enough to:

  • Unblock the App Host implementation.
  • Demonstrate tree views and webviews with real HTTP calls.
  • Evolve toward persistent storage and real mesh/health data later.