AIP-34: WORKSPACE.md — agentworkspace/v1 (workspace identity manifest)
A markdown + frontmatter format for declaring a workspace's identity — globally addressable id, owner, storage choice, defaults, publish posture. The root manifest of every AIP-organized workspace; pairs with STORAGE.md (AIP-35) for the storage policy block.
| Field | Value |
|---|---|
| AIP | 34 |
| Title | WORKSPACE.md — agentworkspace/v1 (workspace identity manifest) |
| Status | Draft |
| Type | Schema |
| Domain | workspace.sh |
| Requires | AIP-1, AIP-2, AIP-19 (SECRETS), AIP-35 (STORAGE), AIP-36 (SANDBOX) |
Abstract
WORKSPACE.md is a markdown-with-frontmatter file format that
packages a single workspace's identity — its globally
addressable id, owner, name, description, storage choice,
defaults, and publish posture. It is the root manifest of every
AIP-organized workspace and the entry point hosts read first when
materializing a workspace.
The format pairs with two sibling blocks that mirror Mastra's
Workspace = filesystem + sandbox shape:
- STORAGE.md (AIP-35) — where the bytes live (filesystem). Required.
- SANDBOX.md (AIP-36) — where shell commands run (compute). Optional — workspaces MAY be storage-only.
Both fields accept inline or ref form.
The file is human-authored, version-controlled, machine-parseable, and grep-able — same posture as SKILL.md, TOOL.md, WORKFLOW.md, CODE.md.
Motivation
Workspaces today exist implicitly: a row in some app's database
points at a folder somewhere, and conventions emerge — .tools/,
.workflows/, .skills/, .runs/. The folder is real, the
conventions are real, but the workspace itself has no manifest.
That implicit shape blocks four real workflows:
-
No portable identity. "What is this workspace? Whose is it? Where do its bytes live?" answers require querying the host's database. There's no manifest a reviewer can read.
-
Storage choice is hidden. Whether a workspace is on Supabase, S3, GitHub, or local disk is a host config detail. The user can't override it without app-side changes.
-
No publishability. Workspaces can't be templates, exports, or registry citizens because they have no addressable id and no manifest to publish.
-
Hosts can't enumerate workspaces uniformly. A multi-tenant host with N workspaces per owner has to special-case its app schema to list, switch, or migrate them. With a root manifest,
find . -name "WORKSPACE.md" -maxdepth 2enumerates the substrate.
WORKSPACE.md makes the workspace a first-class declared entity.
Design principles
-
One file at the root. A workspace is a folder containing exactly one
WORKSPACE.mdat its root. No subfolder counts. -
Identity is globally addressable.
idfollows the form@<owner-slug>/<workspace-slug>. Same posture as designkit / canvakit / agentproto manifests. The address is resolvable across hosts that share an owner namespace. -
Storage policy is its own block. WORKSPACE.md does NOT redefine storage configuration. It either inlines a STORAGE.md block (AIP-35) for one-off cases, or refs a STORAGE.md file / registry slug for reusable policies.
-
Optional for backward compatibility. A workspace without a WORKSPACE.md is still a workspace — hosts fall back to defaults inferred from their database row. WORKSPACE.md is the upgrade path to declared identity, not a hard gate.
-
Manifest wins on conflict. When both a WORKSPACE.md and a host-side row exist, the manifest body is the source of truth. The row caches hot fields (id, current provider) for routing efficiency only.
-
Owner is descriptive, not load-bearing for FKs. The
owner.{type, id, slug}block describes who owns this workspace for human and registry consumption. The DB shape on any host that persists workspaces uses typed FK tables per owner kind (e.g.guild_workspaces,user_workspaces,org_workspaces) — not polymorphicowner_typecolumns. -
Publish posture is opt-in. A workspace declares
publish.template: trueif it intends to be cloneable. The declaration is the input to a downstream registry; this AIP does not specify the registry itself.
Specification
File location
<workspace-root>/
WORKSPACE.md ← this AIP, exactly one
.tools/<id>/TOOL.md ← AIP-14
.drivers/<id>/DRIVER.md ← AIP-30
.workflows/<id>/WORKFLOW.md ← AIP-15
.code/<id>/CODE.md ← AIP-26
.skills/<id>/SKILL.md ← AIP-3
storage/<slug>.STORAGE.md ← AIP-35 (if separated)
... other AIP folders ...Frontmatter
YAML frontmatter, delimited by --- lines. All fields are
case-sensitive.
Required fields
| Field | Type | Description |
|---|---|---|
schema | string | Always workspace/v1. |
id | string | Globally addressable id: @<owner-slug>/<workspace-slug>. Lowercase, digits, dashes. |
version | semver string | Spec version of THIS file. Bump on breaking shape change. |
name | string | Human-readable display name. 1–80 chars. |
owner | object | { type: "guild" | "user" | "org", id: string, slug: string }. The slug participates in the addressable id above. |
storage | object | Filesystem policy: either { inline: <STORAGE.md frontmatter body> } or { ref: "<path or registry slug>" }. See AIP-35. |
Optional fields
| Field | Type | Default | Description |
|---|---|---|---|
description | string | "" | One-paragraph purpose. ≤2000 chars. |
sandbox | object | (none) | Sandbox policy: either { inline: <SANDBOX.md frontmatter body> } or { ref: "<path or registry slug>" } or { file: "<path>" }. See AIP-36. Optional — when absent, command-execution tools MUST be unavailable. |
code | object | (none) | Code policy: either { inline: <CODE.md frontmatter body> } or { ref: "<path or registry slug>" } or { file: "<path>" }. See AIP-26. Optional — when present, the workspace IS a code-workspace / repo (see "Workspace as repo" below). |
identity | object | array | (none) | AIP-23 identity-ref block — default identity for all sub-blocks of this workspace. Sub-blocks (storage, sandbox, code) override. See AIP-23 identity-ref. |
policy | object | array | (none) | AIP-38 POLICY block — access grants, defaults, limits, requirements scoped to this workspace. Composable: inline / ref / file, OR an array that composes (most-restrictive wins). Grants on AIP-39 ACTION ids. See AIP-38 POLICY.md. |
defaults | object | {} | { read_only: bool } and other workspace-wide defaults. |
publish | object | { template: false, visibility: "private" } | Publish posture: { template: bool, registry?: string, visibility: "private" | "unlisted" | "public" }. |
tags | string[] | [] | Free-form discovery tags. |
created_at | string (ISO 8601) | none | Creation timestamp. |
metadata | object | {} | Free-form. Authors MAY stash adapter-specific hints under namespaced keys. |
Body
Markdown body following the frontmatter. Recommended sections:
## Description— long-form purpose, what's in this workspace, who maintains it.## Layout— non-default folder conventions (e.g. "this workspace also uses.briefs/for content drafts").## Maintainers— humans + agents responsible.
The body is informational. Hosts MUST function reading only the frontmatter.
The defineWorkspace standard signature
defineWorkspace(definition: WorkspaceDefinition): WorkspaceHandle
interface WorkspaceDefinition {
schema: "workspace/v1"
id: string // "@<owner>/<slug>"
version: string
name: string
description?: string
owner: {
type: "guild" | "user" | "org"
id: string
slug: string
}
storage:
| { inline: StorageDefinition } // AIP-35 (required)
| { ref: string } // path or registry slug
| { file: string } // workspace-relative path
sandbox?:
| { inline: SandboxDefinition } // AIP-36 (optional)
| { ref: string }
| { file: string }
code?:
| { inline: CodeDefinition } // AIP-26 (optional — workspace as repo)
| { ref: string }
| { file: string }
identity?: // AIP-23 identity-ref
| IdentityRef // single
| IdentityRef[] // multi-attribution
defaults?: { readOnly?: boolean }
publish?: {
template?: boolean
registry?: string
visibility?: "private" | "unlisted" | "public"
}
tags?: string[]
createdAt?: string
metadata?: Record<string, unknown>
}Conformance rules
-
Exactly one WORKSPACE.md per workspace root. Multiple files at root is a spec violation; nested ones are ignored.
-
idis immutable. Once set, never changes. Renaming the workspace mutatesname, neverid. Hosts that need to changeidMUST treat the result as a new workspace. -
owner.slugand the slug suffix ofidMUST match. A workspace at@acme-corp/marketing-opsMUST haveowner.slug = "acme-corp". Validators MUST reject mismatches. -
storage.inlineandstorage.refare mutually exclusive. Exactly one form per WORKSPACE.md. -
No I/O at parse time. Reading WORKSPACE.md MUST NOT trigger credential resolution, network calls, or storage instantiation. Hosts materialize storage lazily.
Stable identity
id + version together form the workspace's stable identity.
A breaking change to the manifest shape (a storage provider
swap, an owner reassignment, a schema upgrade that drops fields)
SHOULD bump version.
Address resolution
Hosts that support cross-host addressable workspaces MUST
implement an address resolver that takes @<owner-slug>/<slug>
and returns either a workspace handle or not-found:
resolveWorkspaceAddress(addr: string): Promise<WorkspaceHandle | null>Single-app hosts resolve trivially: look up <owner-slug> in
the appropriate owner table (guilds / users / orgs), then JOIN
the matching workspace table by <slug>. Multi-app hosts iterate
owner kinds.
The slug lives on the workspace row; the owner_slug lives on
the owner row. No denormalization is required.
Workspace as repo (codespace pattern)
A workspace is just a folder of files plus a manifest. When that folder happens to be a git repo (storage.provider = github) AND has a CODE.md attached, the workspace plays the role of a codespace — a runnable code project the agent (or the user) can iterate on.
There is no separate "CODESPACE.md" primitive. The pattern is:
# WORKSPACE.md at the root of acme/marketing-bot repo
schema: workspace/v1
id: "@acme/marketing-bot"
version: 1.0.0
name: "Marketing Bot"
owner: { type: org, id: "01HQ8VR...", slug: acme }
storage:
inline:
provider: github
config: { owner: acme, repo: marketing-bot, branch: main }
sync:
pull: { on: turn-start, ttl_seconds: 30 }
commit: { on: per-turn, message_template: "{{operator}}: {{summary}}" }
push: { on: per-conversation, branch_policy: per-conversation, pr_policy: auto }
sandbox:
inline:
provider: mastra-e2b
config: { template: "code-interpreter" }
mounts:
- source: workspace
at: /workspace
mode: read-write
code:
file: "./CODE.md" # the workspace IS this code module
identity:
- { ref: "operator://current" }
- { ref: "user://current", role: "co-author" }Mounting workspaces of this kind into other workspaces (via the
host's mount/composition mechanism — typically the host's analog of
Mastra's Workspace.mounts) gives the "codespace mounted at
/marketing-bot/" experience: the parent workspace's agent can
read/write the marketing-bot repo's files at a path prefix, with
all the sync semantics declared in this WORKSPACE.md flowing
through.
The "codespace" terminology is descriptive, not normative.
Implementations MAY surface UI affordances ("This workspace is a
code project") when code is present, but this AIP does not
mandate any specific behaviour beyond "the workspace declares a
CODE.md".
Backward compatibility
A workspace without a WORKSPACE.md is still a valid workspace. Hosts MUST fall back to defaults derived from their persistence layer. Adding a WORKSPACE.md is additive — it never changes the workspace's address (which the host derives from its row).
To migrate a host into the AIP-34 model:
- On workspace creation, write a default WORKSPACE.md derived from the host's row.
- On read, prefer the manifest body over the row fields it covers.
- On row mutation, re-emit the manifest to keep them in sync.
Example
---
schema: workspace/v1
id: "@acme-corp/marketing-ops"
version: 1.0.0
name: Acme Marketing Ops
description: |
Marketing automation workspace for Acme Corp — content briefs,
publishing routines, brand-guidelines kit.
owner:
type: org
id: 01HQ8VRZX5WNNGCV6YD2N7B6RX
slug: acme-corp
storage:
ref: "@acme-corp/shared-s3-policy"
defaults:
read_only: false
publish:
template: false
visibility: private
tags: [marketing, content, brand]
created_at: 2026-05-02T10:00:00Z
---
## Description
The marketing team's primary workspace. Brand kits live under
`.designs/`, content briefs under `.briefs/`, automation routines
under `.workflows/`.
## Maintainers
- Operator `bob` (marketing director)
- Operator `eve` (content strategist)
- Routine `weekly-report` (every Monday 09:00 UTC)Security considerations
WORKSPACE.md is declarative: a malicious manifest can lie
about owner or id. Hosts MUST treat the manifest as untrusted
input and verify ownership against their persistence layer at
read time. The manifest's owner.id MUST match the host row's
owner FK; mismatches MUST be flagged.
storage.ref to a registry slug crosses a trust boundary. Hosts
SHOULD validate that the resolving registry policy is acceptable
under workspace-owner policy before instantiating the backing
storage.
publish.visibility: "public" does NOT auto-publish. The
declaration is input to a downstream registry; the host decides
whether and when to honour it.
Open questions
-
Mounting workspaces into other workspaces. A future extension may allow a workspace to declare
mounts: [{ at: "/external/lib", source: "@vendor/lib" }]. Defer until concrete need. -
Workspace versioning across hosts. When a workspace declared at host A is published and forked at host B, what's the lineage record? Likely a separate
LINEAGE.mdAIP. -
Per-workspace policy bundles. Workspaces may want to declare default approval policies, default agent identity bindings, default compute budgets. Whether these live in WORKSPACE.md or sibling manifests is an open call.
See also
- AIP-3 — SKILL.md — sibling manifest the workspace hosts
- AIP-14 — TOOL.md — sibling manifest the workspace hosts
- AIP-15 — WORKFLOW.md — sibling manifest the workspace hosts
- AIP-19 — SECRETS.md — referenced from STORAGE.md.auth
- AIP-26 — CODE.md — sibling manifest the workspace hosts
- AIP-30 — DRIVER.md — sibling manifest the workspace hosts
- AIP-23 — IDENTITY.md (identity-ref) — referenced from
identity? - AIP-26 — CODE.md — referenced from
code?(workspace-as-repo) - AIP-35 — STORAGE.md — filesystem policy block
- AIP-36 — SANDBOX.md — sandbox/compute policy block
- AIP-37 — LIFECYCLE.md — event vocabulary referenced by sub-block lifecycles
Resources
Supporting artifacts for AIP-34. Links open the file on GitHub — markdown and JSON render natively in GitHub's viewer. Browse the full resource tree →
AIP-33: SDK.md — agentsdk/v1 (in-process SDK driver specialisation)
A markdown + frontmatter format for declaring an in-process SDK driver — an npm/pip/cargo/go package that exposes named functions implementing one or more abstract TOOL contracts directly in the host runtime, without subprocess spawn or network hop. Specialises AIP-30 DRIVER for the `kind: sdk` case.
AIP-35: STORAGE.md — agentstorage/v1 (storage policy block)
A composable schema block defining the `storage` field — provider, config, sync semantics, auth ref, exclude rules — for any manifest that names a backing store. Reused by WORKSPACE.md (AIP-34) and any future manifest that names persistent state. Inline or ref, mirroring AIP-17 RUNNER and AIP-19 SECRETS.