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.poly contains 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 run aborts 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 Acme
  • acme-staging-1 – staging environment
  • startup-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.

→ Best practices

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 UI
  • gitlab – opens the GitLab repository
  • clone – clones the repository locally (equivalent to polycrate 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 path
  • status – fetches the workspace status
  • git-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

  1. Clear separation: workspace files (workspace.poly, blocks/, artifacts/) are infrastructure configuration, not source code.

  2. Never store secrets unencrypted: enable workspace encryption before committing.

  3. Use Git hooks: Polycrate can automatically install pre-commit hooks that warn about unencrypted secrets:

    polycrate workspace encrypt --install-git-hook
    

  4. 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"
    

  5. 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