nullstone-config-files
Teaches Claude to produce schema-valid .nullstone/*.yml files with a consistent style, resolving unknown modules via the Nullstone MCP before emitting blocks.
Catalog version this page describes: v0.1.0 (source)
Triggers
Claude invokes this skill when:
- You edit any YAML under
.nullstone/— single-stack or multi-stack. - You create a new
.nullstone/directory from scratch. - You ask to "add a database to Nullstone", "wire up the cluster", "set an env var for the app", "add a capability", "override for preview/staging", "add a Slack notification on deploy".
- You mention a Nullstone block type (app, datastore, cluster, cluster namespace, network, domain, subdomain, ingress) in the context of config files.
It does not trigger for module authoring (Terraform source code), Nullstone CLI invocations, or UI-only configuration.
What it does
When you ask Claude to add or modify a block, the skill:
- Resolves the module via the MCP —
modules_findto discover candidates,modules_describeto get the module's category and expected vars/connections/capabilities. - Places the block under the correct top-level key based on the module category:
app→appsdatastore→datastoresnetwork→networksdomain→domainssubdomain→subdomainsingress→ingressescluster→clusterscluster-namespace→cluster_namespaces- other/generic →
blocks
- Emits the block following the style rules below.
- Sanity-checks against the bundled JSON schema.
If the MCP is unavailable, the skill asks you to confirm the module and category rather than guessing — placing a block under the wrong top-level key silently breaks Nullstone.
Style rules it enforces
Field order inside a block
module
module_version
connections
vars
environment # apps only
capabilities # apps onlyTop-level key order in the document
events
domains
subdomains
ingresses
apps
datastores
cluster_namespaces
clusters
networks
blocksevents sits on top because it's orthogonal; the rest is a dependency order (consumers above providers) so a reader sees the "what" before the "how".
module_version
Omitted unless you explicitly ask Claude to pin a version.
Capabilities
Map form by default:
capabilities:
postgres:
module: nullstone/aws-postgres-access
connections:
postgres: dbArray form (- name: postgres, …) is only used when you explicitly request it, or when namespace-based disambiguation is needed for colliding capabilities on the same app.
Connection targets
Shortest string form. Segments matching the file's own scope are omitted.
| Situation | Form |
|---|---|
| Same stack, same env | block-name |
| Same stack, different env | env-name.block-name |
| Different stack, different env | stack-name.env-name.block-name |
Shared infra in global stack/global env | global.global.block-name |
The object form ({ block_name, env_name, stack_name }) is only used on explicit request.
Overlay rules
In <env>.yml / previews.yml:
- Include only fields that differ from the base.
- Omit
moduleunless you're changing it. environmententries are additive — overlays append, they don't replace.
Canonical examples
Global single-stack baseline
# Path: .nullstone/config.yml
version: "0.1"
subdomains:
api-subdomain:
module: nullstone/aws-subdomain
connections:
domain: global.global.acme-domain
apps:
api:
module: nullstone/aws-fargate-service
connections:
cluster-namespace: namespace0
subdomain: api-subdomain
vars:
num_tasks: 1
cpu: 256
memory: 512
environment:
LOG_LEVEL: info
BASE_URL: "{{ NULLSTONE_ENV }}.acme.com"
capabilities:
postgres:
module: nullstone/aws-postgres-access
connections:
postgres: db
datastores:
db:
module: nullstone/aws-rds-postgres
connections:
network: network0
vars:
postgres_version: "16"
instance_class: "db.t3.medium"
allocated_storage: 50
cluster_namespaces:
namespace0:
module: nullstone/aws-fargate-namespace
connections:
cluster: fargate0
clusters:
fargate0:
module: nullstone/aws-fargate
connections:
network: network0
networks:
network0:
module: nullstone/aws-network
vars:
cidr: "10.0.0.0/16"
private_subnets:
- "10.0.1.0/24"
- "10.0.2.0/24"
public_subnets:
- "10.0.101.0/24"
- "10.0.102.0/24"Preview overlay (diffs only)
# Path: .nullstone/previews.yml
version: "0.1"
apps:
api:
environment:
LOG_LEVEL: debug
datastores:
db:
vars:
instance_class: "db.t3.small"
allocated_storage: 20Stack-scoped with cross-stack connection
# Path: .nullstone/stacks/web/config.yml
version: "0.1"
subdomains:
api-subdomain:
module: nullstone/aws-subdomain
connections:
domain: global.global.acme-domain
apps:
api:
module: nullstone/aws-fargate-service
connections:
cluster-namespace: namespace0
subdomain: api-subdomain
vars:
num_tasks: 2
cpu: 512
memory: 1024
cluster_namespaces:
namespace0:
module: nullstone/aws-fargate-namespace
connections:
cluster: fargate0
clusters:
fargate0:
module: nullstone/aws-fargate
connections:
network: network0Overriding the skill
Claude will deviate from the style rules when you explicitly ask — e.g. "use the array form for capabilities here", "pin module_version", "use the full object form for this connection so future readers see the env explicitly". The skill treats your instructions as authoritative.
When it might miss
- If the MCP isn't installed or
modules_find/modules_describereturns nothing, the skill falls back to asking you. Don't guess a top-level key from the module name alone — always confirm the category. - If you're editing an existing file that already uses a different style (e.g. array-form capabilities), Claude will match the existing style unless you tell it to reformat.
Bundled reference material
The skill ships with:
- The IaC JSON schema (
config.0.1.json). - A snapshot of the IaC docs — the same pages you see under /gitops/iac/, pinned to a specific fetch date.
- Three canonical examples.
See also
- IaC overview — the authoring contract the skill follows.
- Editor integration — schema-driven autocomplete for human edits.
- Catalog source on GitHub