Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- [Developer Environment](./framework/developer_environment/overview.md)
- [Configuration](./framework/developer_environment/toml.md)
- [External Environment Configuration](./framework/developer_environment/toml_external_env_integration.md)
- [Extending a Generated Environment (AGENTS.md)](./framework/developer_environment/agents.md)
- [Basic Usage](./framework/getting_started.md)
- [Getting Started](./framework/getting_started.md)
- [Advanced Usage](./framework/configuration.md)
Expand Down
94 changes: 94 additions & 0 deletions book/src/framework/developer_environment/agents.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Extending a Generated Environment (AGENTS.md)

Every environment generated by `ctf gen env` ships an `AGENTS.md` file at its root that teaches
AI coding agents how the environment is structured and how to extend it following the existing
patterns.

This page reproduces that guide for reference — useful if your environment was generated before
`AGENTS.md` was added, or if you just want to read it here. Names below use the generator defaults
(`pcli` for the CLI, `myproduct` for the product); substitute your own if you chose different ones.

> If you work with an AI agent, point it at `devenv/AGENTS.md`. If that file is missing (older
> environment), copy the content below into `devenv/AGENTS.md`, replacing `pcli`/`myproduct` with
> your CLI and product names.

---

A generated Chainlink developer environment composes Docker containers (blockchains, CL node sets,
fakes, custom services) and exposes an interactive CLI called `pcli` with autocompletion. Keep
changes minimal and idiomatic.

## Layout

| Path | What it is |
|------|------------|
| `cmd/pcli/pcli.go` | CLI commands (cobra). Add commands here. |
| `cmd/pcli/completion.go` | Shell autocompletion suggestions. Mirror every CLI command here. |
| `environment.go` | Wiring: reads `Cfg` from TOML, spins up all Docker components in order. |
| `services/svc.go` | Template for a custom Docker component (container). Copy this to add services. |
| `products/myproduct/configurator.go` | Product logic: CL node configs, secrets, jobs & contracts. |
| `products/myproduct/*.toml` | Product config profiles (`basic.toml`, `soak.toml`). |
| `fakes/main.go` | HTTP fakes for 3rd-party dependencies (separate Docker image + go.mod). |
| `tests/myproduct/{func,perf}_test.go` | System-level functional / performance (WASP) tests. |
| `env.toml` | Default environment config: blockchains, nodesets, services, products. |
| `Justfile` | Build recipes: `just cli`, `just build-fakes`. |
| `dashboards/myproduct.json` | Grafana dashboard, auto-uploaded on `up`. |

## Build & run

```bash
just cli # rebuild the pcli binary after editing cmd/pcli/*
pcli sh # enter the interactive shell (autocompletion)
up # spin up env (inside shell); or: pcli up
test func # run functional tests; test perf for performance
```

Config is selected via the `CTF_CONFIGS` env var, a comma-separated list of TOML files
(e.g. `env.toml,products/myproduct/basic.toml`). `up`/`restart` set this for you.

## Add a CLI command

Commands are [cobra](https://github.com/spf13/cobra) commands in `cmd/pcli/pcli.go`.

1. Declare a `*cobra.Command` var with `Use`, `Aliases`, `Short`, and a `RunE` func.
2. Register it in `init()` with `rootCmd.AddCommand(myCmd)` (or `parent.AddCommand(child)` for subcommands; set flags via `parent.PersistentFlags()` — see `bsCmd`/`obsCmd`).
3. **Always** add a matching entry in `cmd/pcli/completion.go`:
- top-level command → add to `getCommands()`
- subcommands/args → add a `case "<cmd>":` in `getSubCommands()`
4. `just cli` to rebuild.

Business logic lives in the `framework` package (e.g. `framework.ObservabilityUp()`,
`framework.BlockScoutUp()`); the CLI is a thin wrapper. Prefer reusing framework helpers.

## Wire up a new Docker component (service)

1. Copy `services/svc.go` to `services/<name>.go`. Each service = an `Input` struct (with
`Default()`), an `Output` struct (with an `out` TOML tag), and a `New<Name>(in)` deploy func
using `testcontainers`. Use `framework.DefaultTCLabels()`, `framework.DefaultNetworkName`,
and static host ports.
2. Add the input field to the `Cfg` struct in `environment.go` (with a `toml` tag).
3. Call `New<Name>(in.<Field>)` inside `NewEnvironment()`, alongside `services.NewService`.
4. Add its config block to `env.toml`.
5. A deployed service must guard on `in == nil || in.Out != nil` to stay idempotent (see `NewService`).

## Add a product

1. Copy `products/myproduct/` → `products/<newproduct>/` and `tests/myproduct/` → `tests/<newproduct>/`.
2. Implement the `Product` interface (`interface.go`) in `configurator.go`:
`Load`, `Store`, `GenerateNodesConfig`, `GenerateNodesSecrets`, `ConfigureJobsAndContracts`.
3. Register it in `newProduct(name)` in `environment.go` with a new `case`.
4. Add a `[[products]]` entry (with `name`, `instances`) in `env.toml`.
Multiple instances → your `ConfigureJobsAndContracts` must handle `productIdx`.

## Add / change fakes (3rd-party HTTP)

Edit `fakes/main.go` (its own Docker image + `go.mod`), then `just build-fakes`.
Tests reach fakes via the `fake_server` output; see `tests/myproduct/func_test.go`.

## Conventions (do not break)

- Docker components: static ports, `framework.DefaultTCLabels()`, `framework.DefaultNetworkName`.
- Config is TOML-driven; every new component/product needs a `Cfg`/`env.toml` entry.
- Idempotent deploys: skip if output already set.
- Every CLI command has a completion entry.
- Address any `// TODO` comments the generator left before shipping.
5 changes: 4 additions & 1 deletion book/src/framework/developer_environment/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ ctf gen -h
ctf gen env --cli pcli --product-name MyProduct --output-dir devenv --nodes 4
```

Follow further instructions in `devenv/README.md`
Follow further instructions in `devenv/README.md`.

Use `devenv/AGENTS.md` if you work with AI. You can also read that guide here:
[Extending a Generated Environment](./agents.md) (handy for environments generated before `AGENTS.md` was added).

## Remote Environment

Expand Down
2 changes: 2 additions & 0 deletions book/src/framework/generate.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ ctf gen env --cli pcli --product-name MyProduct --output-dir devenv --nodes 4

Follow further instructions in `devenv/README.md`

The generated environment also ships an `devenv/AGENTS.md` — a short guide that teaches AI coding agents how the environment is structured and how to extend it (add CLI commands + completion, wire up new Docker components, add products/fakes) following the existing patterns.

# Generating Infrastructure Testing Template

If you have Chainlink infrastructure already deployed it's useful to generate a workload + chaos suite template.
Expand Down
2 changes: 1 addition & 1 deletion framework/examples/myproject/chaos/chaos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@
[[nodesets.node_specs]]

[nodesets.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.17.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"
2 changes: 1 addition & 1 deletion framework/examples/myproject/chaos/chaos_gas.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@
[[nodesets.node_specs]]

[nodesets.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.17.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"
2 changes: 1 addition & 1 deletion framework/examples/myproject/chaos/chaos_reorg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@
[[nodesets.node_specs]]

[nodesets.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.17.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"
2 changes: 1 addition & 1 deletion framework/examples/myproject/fake.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@
[[nodesets.node_specs]]

[nodesets.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.17.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"
2 changes: 1 addition & 1 deletion framework/examples/myproject/fake_docker.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@
[[nodesets.node_specs]]

[nodesets.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.17.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"
2 changes: 1 addition & 1 deletion framework/examples/myproject/fork_plus_offchain.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@
[[nodesets.node_specs]]

[nodesets.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.17.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"
2 changes: 1 addition & 1 deletion framework/examples/myproject/performance_baseline.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@
[[nodesets.node_specs]]

[nodesets.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.17.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"
2 changes: 1 addition & 1 deletion framework/examples/myproject/quick-deploy.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
[[nodesets.node_specs]]

[nodesets.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.17.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"
2 changes: 1 addition & 1 deletion framework/examples/myproject/scalability.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@
[[nodesets.node_specs]]

[nodesets.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.17.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"
2 changes: 1 addition & 1 deletion framework/examples/myproject/smoke.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@
[[nodesets.node_specs]]

[nodesets.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.17.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"
2 changes: 1 addition & 1 deletion framework/examples/myproject/smoke_import.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@
[[nodesets.node_specs]]

[nodesets.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.17.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
[[nodesets.node_specs]]

[nodesets.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.17.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"

[nodesets.node_specs.node.resources]
cpus = 1
Expand Down
10 changes: 5 additions & 5 deletions framework/examples/myproject/smoke_sui.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,24 @@
[[nodesets.node_specs]]

[nodesets.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.17.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"

[[nodesets.node_specs]]

[nodesets.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.17.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"

[[nodesets.node_specs]]

[nodesets.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.17.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"

[[nodesets.node_specs]]

[nodesets.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.17.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"

[[nodesets.node_specs]]

[nodesets.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.17.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"
10 changes: 5 additions & 5 deletions framework/examples/myproject/upgrade.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,24 @@
[[nodesets.node_specs]]

[nodesets.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.16.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"

[[nodesets.node_specs]]

[nodesets.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.16.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"

[[nodesets.node_specs]]

[nodesets.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.16.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"

[[nodesets.node_specs]]

[nodesets.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.16.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"

[[nodesets.node_specs]]

[nodesets.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.16.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"
4 changes: 2 additions & 2 deletions framework/examples/myproject/upgrade_multi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
[[nodeset_a.node_specs]]

[nodeset_a.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.17.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"

[nodeset_b]
http_port_range_start = 10100
Expand All @@ -34,4 +34,4 @@
[[nodeset_b.node_specs]]

[nodeset_b.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.17.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"
20 changes: 10 additions & 10 deletions framework/examples/myproject/upgrade_multi_each.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,27 @@
[[nodeset_a.node_specs]]

[nodeset_a.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.16.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"

[[nodeset_a.node_specs]]

[nodeset_a.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.16.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"

[[nodeset_a.node_specs]]

[nodeset_a.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.16.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"

[[nodeset_a.node_specs]]

[nodeset_a.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.16.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"

[[nodeset_a.node_specs]]

[nodeset_a.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.16.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"

[nodeset_b]
http_port_range_start = 10000
Expand All @@ -54,24 +54,24 @@
[[nodeset_b.node_specs]]

[nodeset_b.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.16.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"

[[nodeset_b.node_specs]]

[nodeset_b.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.16.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"

[[nodeset_b.node_specs]]

[nodeset_b.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.16.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"

[[nodeset_b.node_specs]]

[nodeset_b.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.16.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"

[[nodeset_b.node_specs]]

[nodeset_b.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.16.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"
2 changes: 1 addition & 1 deletion framework/examples/myproject_cll/jd_nodeset.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@
[[nodesets.node_specs]]

[nodesets.node_specs.node]
image = "public.ecr.aws/chainlink/chainlink:v2.17.0"
image = "public.ecr.aws/chainlink/chainlink:2.49.1-rc.2"
Loading
Loading