Workspaces
A Polycrate workspace is a folder holding configuration, credentials and lifecycle artifacts. Directory layout, .state.poly, workspace init and clone, organization conventions and hybrid code + workspace repositories.
Workspaces
Whatever you build with Polycrate happens inside a workspace. The workspace contains configuration, credentials and lifecycle artifacts that are created when blocks are executed. A workspace is essentially just a folder on your disk that can be versioned and synchronized with git or other mechanisms.
The workspace is assembled using the workspace configuration.
Workspace directory layout
A workspace follows a defined directory structure:
graph TB
WS[my-workspace/] --> Config[workspace.poly]
WS --> Inv[inventory.yml]
WS --> Secrets[secrets.poly]
WS --> DF[Dockerfile.poly]
WS --> CL[CHANGELOG.poly]
WS --> Blocks[blocks/]
WS --> Arts[artifacts/]
Blocks --> B1[block-1/]
Blocks --> B2[block-2/]
B1 --> BP1[block.poly]
B1 --> CL1[CHANGELOG.poly]
B1 --> RM1[README.md]
B1 --> IN1[install.yml]
B1 --> T1[templates/]
Arts --> SecretsDir[secrets/]
Arts --> BlockArts[blocks/]
SecretsDir --> IdRsa[id_rsa]
SecretsDir --> IdRsaPub[id_rsa.pub]
SecretsDir --> WSKube[kubeconfig.yml]
SecretsDir --> B1Sec[block-1/]
B1Sec --> DbPwd[database.pwd]
BlockArts --> BA1[block-1/]
Important directories and files
Workspace root
| File/directory | Required | Sensitive | Description |
|---|---|---|---|
workspace.poly |
✅ | No | Workspace configuration |
inventory.yml |
No | No | Ansible inventory in the workspace root (default file name; a different name is possible via the workspace inventory key) |
secrets.poly |
No | ⚠️ Yes | Sensitive block configuration (unencrypted) |
secrets.poly.age |
No | No | Encrypted version of secrets.poly |
.workspace-encrypted |
No | No | Encryption marker |
.state.poly |
No | No | Operational state (CLI/API/operator coordination) |
Dockerfile.poly |
No | No | Custom Docker image for the workspace |
CHANGELOG.poly |
No | No | Workspace changelog for release tracking |
blocks/ |
No | No | Template and local blocks |
artifacts/ |
No | Partially | Files generated and required by blocks |
blocks/ – block directories
Every block has its own subdirectory under blocks/:
| File/directory | Required | Description |
|---|---|---|
block.poly |
✅ | Block configuration and actions |
CHANGELOG.poly |
✅ | Version history of the block |
README.md |
No | Documentation of the block |
*.yml |
No | Ansible playbooks (directly in the block root) |
templates/ |
No | Jinja2 templates for Ansible |
artifacts/secrets/ – sensitive files
| Path | Description |
|---|---|
id_rsa |
SSH private key |
id_rsa.pub |
SSH public key |
kubeconfig.yml |
Kubernetes configuration (workspace-wide) |
<BLOCKNAME>/ |
Block-specific secrets |
artifacts/blocks// – block artifacts
artifacts/blocks/<BLOCKNAME>/ holds files produced by the respective block (varies per block). A file hosts_cache.json is no longer created by current Polycrate versions (older workspaces may still contain it).
→ Detailed documentation on artifacts
.state.poly – operational state
.state.poly is updated automatically by the CLI after every action run and stored in the workspace root. It serves the coordination between CLI, API and operator: which component manages which block.
# .state.poly – example
cli:
polycrate-operator:
last_action: install
last_action_at: "2026-02-25T10:00:00Z"
last_action_exit_code: 0
api:
polycrate-operator:
managed_since: "2026-02-01T00:00:00Z"
- No config override –
.state.polycontains no block content, only operational state. - Git-versioned – the file is committed and visible to everyone.
- Conflict detection – if a block is managed by both the CLI and the API,
polycrate runaborts with an error message. - A missing file is not an error – the first action run creates the file.
Resolving a conflict: a block may only be managed by one component. In case of a conflict, choose one of these paths:
| Goal | Procedure |
|---|---|
| API takes over | Remove the block from workspace.poly and secrets.poly → the next action run removes the entry from .state.poly[cli] → the API can register the block. |
| CLI keeps it | The API deregisters the block (remove the .state.poly[api] entry) → add the block to workspace.poly → the next action run registers it in .state.poly[cli]. |
Initializing a workspace
# Create a new workspace
polycrate workspace init
# or short form:
polycrate init
# With SSH keys (stored in artifacts/secrets/)
polycrate workspace init --with-ssh-keys
# With Git repository
polycrate workspace init --git
Cloning a workspace (API)
Fetch a workspace that already exists in the API locally — without creating it anew:
# Default: ~/.polycrate/workspaces/acme/acme-production-1
polycrate workspace clone acme/acme-production-1
# Into the current directory as a subfolder
cd ~/.polycrate/workspaces/acme
polycrate workspace clone acme/acme-production-1 .
The CLI asks the API for git_ssh_url and clones via go-git (SSH). Without access to the workspace or without a Git URL the command fails. If the target directory already exists, it aborts.
PATH is the parent directory; the workspace folder is always named after the workspace (<PATH>/<name>). Without PATH, --workspace-dir applies (default $HOME/.polycrate/workspaces).
Prerequisite: API integration and an SSH key that is allowed to access the Git remote.
→ CLI reference: workspace clone
Workspace organization
Recommended directory structure
Workspaces should be organized under $HOME/.polycrate/workspaces/. The recommended segmentation is:
$HOME/.polycrate/workspaces/<organization>/<name>
This is not mandatory – any path works as long as you pass it with --workspace/-w. However, the structure helps especially in combination with the Polycrate API and when you, as a managed service provider, look after many organizations: better overview, clear assignment and appropriate isolation between customer workspaces.
$HOME/.polycrate/workspaces/
├── acme/ # organization: acme
│ ├── acme-production-1/ # name: acme-production-1
│ ├── acme-staging-1/
│ └── acme-development-1/
├── other-org/
│ └── other-org-production-1/
Naming scheme
Workspace names often follow the scheme $org-$purpose-$count:
| Component | Description | Example |
|---|---|---|
$org |
Organization (slugified) | acme |
$purpose |
Purpose/environment | production, staging, dev |
$count |
Number (starting at 1) | 1, 2, 3 |
Examples:
acme-production-1– first production environment of Acmeacme-staging-1– staging environmentstartup-dev-1– development environment
name and organization must also satisfy the same naming rules as all Polycrate identifiers (regex, allowed characters).
→ Naming conventions – central reference including the pattern ^[a-zA-Z]+([-/_]?[a-zA-Z0-9_]+)+$ and examples
Polycrate validates these fields when loading the workspace; violations produce a corresponding error message.
Listing workspaces
The workspace list command shows workspaces from the Polycrate API or from the local workspace directory.
Remote mode (default)
If the Polycrate API is configured, workspaces are loaded from the API:
# API workspaces (default)
polycrate workspace list
# With filter
polycrate workspace list --filter production
# Table output
polycrate workspace list --no-pager
Action menu on selection (remote):
open– opens the workspace in the Polycrate UIgitlab– opens the GitLab repositoryclone– clones the repository locally (equivalent topolycrate workspace clone ORG/WORKSPACE)
Local mode
With --local, workspaces are scanned from ~/.polycrate/workspaces/:
# Local workspaces
polycrate workspace list --local
# With filter
polycrate workspace list --local --filter production
# Detailed table output
polycrate workspace list --local --no-pager -v
Action menu on selection (local):
open– shows workspace details and pathstatus– fetches the workspace statusgit-sync– synchronizes Git changes
→ CLI reference: workspace list
Hybrid workspaces (code + workspace)
In practice, workspaces are often managed together with source code – for example in software projects that are built and deployed with Polycrate. The polycrate-cli itself is such an example: Go source code and Polycrate workspace coexist in the same repository.
Workspace core files
These files and folders belong exclusively to the Polycrate workspace and should be excluded from build processes (Docker, CI/CD):
Required files
| File | Description |
|---|---|
workspace.poly |
Workspace configuration (the only required file) |
Optional workspace files
| File/folder | Description | Sensitive? |
|---|---|---|
secrets.poly |
Unencrypted block secrets | ⚠️ Yes |
secrets.poly.age |
Encrypted secrets | No |
.state.poly |
Operational state (CLI/API/operator) | No |
CHANGELOG.poly |
Workspace changelog | No |
Dockerfile.poly |
Custom container image | No |
.workspace-encrypted |
Encryption marker | No |
inventory.yml |
Ansible inventory in the workspace root (default) | Optional |
Standard directories
| Directory | Description | Sensitive? |
|---|---|---|
blocks/ |
Block definitions | No |
artifacts/ |
Generated files | ⚠️ Partially |
Sensitive files (artifacts/secrets/)
| Path | Description |
|---|---|
artifacts/secrets/id_rsa |
SSH private key |
artifacts/secrets/id_rsa.pub |
SSH public key |
artifacts/secrets/kubeconfig.yml |
Kubernetes config |
artifacts/secrets/<block>/ |
Block-specific secrets |
.gitignore for hybrid workspaces
For hybrid workspaces (code + Polycrate), .gitignore should exclude all sensitive Polycrate files:
# ============================================
# Polycrate Workspace - Sensitive Files
# ============================================
# Unencrypted secrets (NEVER commit!)
secrets.poly
id_rsa
id_rsa.pub
# Sensitive artifacts
artifacts/secrets/*
!artifacts/secrets/*/
!artifacts/secrets/*.age
artifacts/secrets/*/*
!artifacts/secrets/*/*.age
# Kubeconfig (lives under artifacts/secrets/kubeconfig.yml – covered by artifacts/secrets/*)
# The inventory in the workspace root is not sensitive and is committed
# inventory.yml
# ============================================
# Polycrate Workspace - Optional excludes
# ============================================
# Build artifacts (if any)
dist/
# Keep the encryption marker
!.workspace-encrypted
.dockerignore for hybrid workspaces
For Docker builds it is critical that no workspace-specific files end up in the image:
# ============================================
# Polycrate Workspace - exclude EVERYTHING!
# ============================================
# Workspace configuration (not needed at runtime)
workspace.poly
secrets.poly
secrets.poly.age
CHANGELOG.poly
inventory.yml
# Workspace directories
blocks/
artifacts/
# SSH keys (NEVER into the image!)
id_rsa
id_rsa.pub
ssh-passphrase.poly
# Encryption marker
.workspace-encrypted
# Custom Polycrate Dockerfile (not for the app build)
Dockerfile.poly
# ============================================
# Development & Documentation
# ============================================
# Specs and Cursor rules
.specs/
.cursor/
# Documentation
*.md
!README.md # Optional: keep README in the image
# Git
.git/
.gitignore
.gitlab-ci.yml
# IDE
.vscode/
.idea/
Example: polycrate-cli repository
The polycrate-cli demonstrates a hybrid workspace:
polycrate-cli/
├── # === Go source code ===
├── main.go
├── go.mod
├── go.sum
├── cmd/ # CLI commands
├── pkg/ # Packages
│
├── # === Docker ===
├── Dockerfile # Production build
├── Dockerfile.poly # Polycrate container (optional)
│
├── # === Polycrate workspace ===
├── workspace.poly # ← Workspace configuration
├── CHANGELOG.poly # ← Release changelog
├── blocks/ # ← Block definitions
│ ├── app/ # Release management
│ ├── cargo.ayedo.cloud/ # Registry blocks
│ └── ayedo/
│ └── build-tools/
│ ├── golang/ # Go build
│ └── docker/ # Docker build
├── artifacts/ # ← Generated files
│ ├── secrets/ # SSH keys, kubeconfigs
│ └── blocks/ # Block artifacts
│
├── # === Build output ===
├── dist/ # Go binaries + archives
│
├── # === Misc ===
├── .gitignore
├── .dockerignore
├── Makefile
└── README.md
Best practices for hybrid workspaces
-
Clear separation: workspace files (
workspace.poly,blocks/,artifacts/) are infrastructure configuration, not source code. -
Never store secrets unencrypted: enable workspace encryption before committing.
-
Use Git hooks: Polycrate can automatically install pre-commit hooks that warn about unencrypted secrets:
polycrate workspace encrypt --install-git-hook -
CI/CD separation: in CI/CD pipelines the workspace should be handled separately:
# .gitlab-ci.yml example build: script: - polycrate run golang build - polycrate run docker push only: changes: - "**/*.go" - "go.mod" - "Dockerfile" -
Docker multi-stage: use multi-stage builds to make sure only the required binaries end up in the final image:
# Build stage FROM golang:1.23 AS builder COPY . /src RUN go build -o /app/binary . # Runtime stage (minimal, no workspace files) FROM alpine:latest COPY --from=builder /app/binary /usr/local/bin/
Relation to other concepts
- Naming conventions: regex and valid names for
nameandorganization - Blocks: workspaces contain and orchestrate blocks
- Artifacts: persistent data is stored in
artifacts/ - Encryption: secrets are encrypted automatically
- Git integration: version control and synchronization
- Best practices: recommendations for workspace organization