9.2 KiB
Workspaces & Git
The Workspace is a virtual filesystem for the editor surface. Each workspace has a root directory, file operations, optional Git integration, and full-text indexing for code search.
All endpoints require: Auth() middleware (JWT bearer token).
Workspace-scoped endpoints additionally verify ownership via
loadAndAuthorize (user owns workspace, or is member of the
owning team/project/channel).
Workspace CRUD
GET /workspaces → { "data": [...] }
POST /workspaces ← { "name", "owner_type", "owner_id" }
GET /workspaces/:id → workspace object
PATCH /workspaces/:id ← partial update
DELETE /workspaces/:id
Auth: Authenticated user. GET /workspaces returns user-owned
workspaces plus those owned by the user's teams. All :id endpoints
verify the caller can access the workspace's owner entity.
Workspace object:
{
"id": "uuid",
"name": "my-project",
"owner_type": "user",
"owner_id": "uuid",
"status": "active",
"max_bytes": 52428800,
"indexing_enabled": false,
"git_remote_url": "https://github.com/...",
"git_branch": "main",
"git_credential_id": "uuid",
"git_last_sync": "2025-06-15T14:30:00Z",
"file_count": 42,
"total_bytes": 1048576,
"created_at": "2025-06-15T14:30:00Z",
"updated_at": "2025-06-15T14:30:00Z"
}
| Field | Type | Notes |
|---|---|---|
id |
string | UUIDv4 |
name |
string | Max 200 chars |
owner_type |
"user" | "project" | "channel" | "team" |
Owner entity type |
owner_id |
string | UUID of owner entity |
status |
"active" | "archived" | "deleting" |
Lifecycle state |
max_bytes |
int | null | Quota ceiling. Omitted if unset. |
indexing_enabled |
bool | Full-text search indexing |
git_remote_url |
string | null | Git remote. Omitted if unset. |
git_branch |
string | null | Tracked branch. Omitted if unset. |
git_credential_id |
string | null | FK to git credential. Omitted if unset. |
git_last_sync |
string | null | ISO 8601. Omitted if never synced. |
file_count |
int | Computed. Enriched on GET /:id only. |
total_bytes |
int | Computed. Enriched on GET /:id only. |
created_at |
string | ISO 8601 |
updated_at |
string | ISO 8601 |
Note:
root_pathis a server-internal filesystem path and is never exposed in API responses (json tag"-").
Create request:
{
"name": "my-project",
"owner_type": "user",
"owner_id": "uuid",
"max_bytes": 52428800
}
Returns 201 with the workspace object.
PATCH request (all fields optional):
{
"name": "new-name",
"status": "archived",
"max_bytes": 104857600,
"indexing_enabled": true,
"git_remote_url": "https://...",
"git_branch": "develop",
"git_credential_id": "uuid"
}
Returns updated workspace object.
DELETE returns {"ok": true}. Deletion is async (status set to
"deleting", filesystem cleanup runs in background).
File Operations
All paths are relative to the workspace root. File path is passed
as ?path= query parameter.
GET /workspaces/:id/files?path=/ → { "data": [entries] }
GET /workspaces/:id/files/read?path=/a.md → raw file content (binary stream)
PUT /workspaces/:id/files/write?path=/a.md ← raw body (Content-Length required)
DELETE /workspaces/:id/files/delete?path=/a.md
POST /workspaces/:id/files/mkdir?path=/src
Auth: Authenticated user with workspace access.
GET .../files accepts ?recursive=true for deep listing.
Returns {"data": [...]} (never bare array). Nil slice guarded.
File entry (from WorkspaceFile model):
{
"id": "uuid",
"workspace_id": "uuid",
"path": "/src/main.go",
"is_directory": false,
"content_type": "text/x-go",
"size_bytes": 4096,
"sha256": "abc123...",
"index_status": "ready",
"chunk_count": 3,
"created_at": "2025-06-15T14:30:00Z",
"updated_at": "2025-06-15T14:30:00Z"
}
GET .../files/read returns raw file content with appropriate
Content-Type header and Content-Length. This is a binary stream,
not JSON.
PUT .../files/write reads the request body as raw content.
Returns {"ok": true, "path": "/a.md"}. Returns 413 if the
write would exceed the workspace quota.
DELETE .../files/delete accepts ?recursive=true for
directory removal. Returns {"ok": true}.
POST .../files/mkdir returns 201 {"ok": true, "path": "/src"}.
Archive
Download (full workspace as archive):
GET /workspaces/:id/archive/download?format=zip
Auth: Authenticated user with workspace access.
format query param: zip (default), tar.gz, tgz.
Returns binary stream with Content-Disposition: attachment.
Upload (restore from archive):
POST /workspaces/:id/archive/upload
Content-Type: multipart/form-data (field: "file")
Auth: Authenticated user with workspace access.
Accepts .zip, .tar.gz, .tgz archives. Format detected from
filename extension.
Returns:
{
"ok": true,
"files_extracted": 42
}
Reconcile, Stats, Index Status
Reconcile (sync filesystem state with DB metadata):
POST /workspaces/:id/reconcile
Auth: Authenticated user with workspace access.
Returns:
{
"added": 5,
"removed": 2,
"updated": 3
}
Stats:
GET /workspaces/:id/stats
Auth: Authenticated user with workspace access.
Returns a WorkspaceStats object:
{
"workspace_id": "uuid",
"file_count": 42,
"dir_count": 8,
"total_bytes": 1048576,
"max_bytes": 52428800
}
Index status:
GET /workspaces/:id/index-status
Auth: Authenticated user with workspace access.
Returns indexing progress for full-text search:
{
"workspace_id": "uuid",
"indexing_enabled": true,
"status_counts": {
"pending": 3,
"indexing": 1,
"ready": 38,
"error": 0,
"skipped": 2
},
"total_chunks": 156
}
Git Integration
All Git operations are workspace-scoped. Require the workspace to have a Git remote configured.
POST /workspaces/:id/git/clone ← { "url", "branch", "credential_id" }
POST /workspaces/:id/git/pull
POST /workspaces/:id/git/push
GET /workspaces/:id/git/status → GitStatus object
GET /workspaces/:id/git/diff → { "diff": "..." }
POST /workspaces/:id/git/commit ← { "message", "paths": [...] }
GET /workspaces/:id/git/log → { "data": [GitLogEntry] }
GET /workspaces/:id/git/branches → { "branches": [...], "current": "main" }
POST /workspaces/:id/git/checkout ← { "branch": "feature-x" }
Auth: Authenticated user with workspace access.
POST .../git/clone request:
{
"url": "https://github.com/user/repo.git",
"branch": "main",
"credential_id": "uuid"
}
branch and credential_id are optional. Returns {"status": "cloned"}.
POST .../git/pull returns {"status": "pulled"}.
POST .../git/push returns {"status": "pushed"}.
GET .../git/status returns the full GitStatus object:
{
"branch": "main",
"clean": false,
"ahead": 2,
"behind": 0,
"staged": [{ "path": "file.go", "status": "M" }],
"modified": [{ "path": "other.go", "status": "M" }],
"untracked": ["new-file.txt"]
}
GET .../git/diff accepts optional ?path=file.go for single-file diff.
Returns {"diff": "..."} (unified diff string).
POST .../git/commit request:
{
"message": "fix: resolve null pointer",
"paths": ["/src/main.go", "/src/util.go"]
}
paths is optional (commits all staged if omitted). Returns
{"status": "committed"}.
GET .../git/log accepts optional ?n=20 (default 20, max 100).
Returns {"data": [GitLogEntry]}:
{
"data": [
{
"hash": "abc123...",
"short_hash": "abc123",
"author": "Jane Doe",
"date": "2025-06-15T14:30:00Z",
"message": "Initial commit"
}
]
}
GET .../git/branches returns:
{
"current": "main",
"branches": ["main", "develop", "feature-x"]
}
POST .../git/checkout returns
{"status": "checked out", "branch": "feature-x"}.
Git Credentials
User-owned, encrypted credential storage for Git operations. Encrypted with the user's UEK via the platform vault (same pattern as BYOK keys — admins cannot recover).
Independent of workspace scope — credentials are user-level.
POST /git-credentials ← { "name", "auth_type", ... }
GET /git-credentials → { "data": [GitCredentialSummary] }
DELETE /git-credentials/:id
Auth: Authenticated user. Credentials are scoped to the requesting user.
Create request:
auth_type determines which fields are required:
auth_type |
Required fields |
|---|---|
https_pat |
token |
https_basic |
username, password |
ssh_key |
private_key (+ optional passphrase) |
Returns 201 with the credential summary (never exposes encrypted data).
List response:
{
"data": [
{
"id": "uuid",
"name": "GitHub PAT",
"auth_type": "https_pat",
"created_at": "2025-06-15T14:30:00Z"
}
]
}
DELETE returns {"deleted": true}. Returns 404 if credential
not found or not owned by the requesting user.