@calimero-network/agent-skills 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +137 -17
  2. package/SKILL.md +31 -28
  3. package/package.json +1 -1
  4. package/scripts/install.js +3 -3
  5. package/scripts/test.js +6 -15
  6. package/skills/calimero-abi-codegen/SKILL.md +121 -22
  7. package/skills/calimero-abi-codegen/references/abi-format.md +3 -5
  8. package/skills/calimero-abi-codegen/references/generated-output.md +12 -4
  9. package/skills/calimero-abi-codegen/rules/schema-version.md +11 -4
  10. package/skills/calimero-abi-codegen/rules/unique-names.md +2 -6
  11. package/skills/calimero-client-js/SKILL.md +126 -31
  12. package/skills/calimero-client-js/references/auth.md +18 -10
  13. package/skills/calimero-client-js/references/rpc-calls.md +15 -21
  14. package/skills/calimero-client-js/references/sso.md +9 -9
  15. package/skills/calimero-client-js/references/websocket-events.md +73 -92
  16. package/skills/calimero-client-js/rules/camelcase-api.md +10 -7
  17. package/skills/calimero-client-js/rules/token-refresh.md +11 -11
  18. package/skills/calimero-client-py/SKILL.md +25 -13
  19. package/skills/calimero-client-py/references/api.md +41 -43
  20. package/skills/calimero-client-py/references/auth.md +7 -7
  21. package/skills/calimero-client-py/rules/async-usage.md +27 -31
  22. package/skills/calimero-client-py/rules/stable-node-name.md +7 -7
  23. package/skills/calimero-core/SKILL.md +135 -0
  24. package/skills/calimero-core/references/architecture.md +101 -0
  25. package/skills/calimero-core/references/jsonrpc-protocol.md +192 -0
  26. package/skills/calimero-core/references/namespaces-groups.md +94 -0
  27. package/skills/calimero-core/references/storage-types.md +118 -0
  28. package/skills/calimero-core/references/websocket-events.md +142 -0
  29. package/skills/calimero-core/rules/context-is-not-app.md +35 -0
  30. package/skills/calimero-core/rules/crdt-types-only.md +55 -0
  31. package/skills/calimero-desktop/SKILL.md +24 -19
  32. package/skills/calimero-desktop/references/sso-integration.md +2 -2
  33. package/skills/calimero-desktop/rules/sso-fallback.md +3 -2
  34. package/skills/calimero-merobox/SKILL.md +255 -28
  35. package/skills/calimero-merobox/references/ci-integration.md +3 -2
  36. package/skills/calimero-merobox/references/workflow-files.md +7 -5
  37. package/skills/calimero-merobox/rules/docker-required.md +7 -6
  38. package/skills/calimero-meroctl/SKILL.md +68 -0
  39. package/skills/calimero-meroctl/references/commands.md +177 -0
  40. package/skills/calimero-meroctl/references/scripting.md +80 -0
  41. package/skills/calimero-meroctl/rules/call-view-flag.md +28 -0
  42. package/skills/calimero-meroctl/rules/register-node-once.md +34 -0
  43. package/skills/calimero-merod/SKILL.md +49 -0
  44. package/skills/calimero-merod/references/health-endpoints.md +90 -0
  45. package/skills/calimero-merod/references/init-flags.md +84 -0
  46. package/skills/calimero-merod/rules/init-before-run.md +40 -0
  47. package/skills/calimero-merod/rules/port-assignments.md +33 -0
  48. package/skills/calimero-node/SKILL.md +50 -39
  49. package/skills/calimero-node/references/context-lifecycle.md +34 -17
  50. package/skills/calimero-node/references/meroctl-commands.md +89 -99
  51. package/skills/calimero-node/rules/app-vs-context.md +4 -4
  52. package/skills/calimero-registry/SKILL.md +110 -31
  53. package/skills/calimero-registry/references/bundle-and-push.md +99 -34
  54. package/skills/calimero-registry/references/manifest-format.md +56 -35
  55. package/skills/calimero-registry/references/mero-sign.md +10 -9
  56. package/skills/calimero-registry/rules/key-security.md +3 -2
  57. package/skills/calimero-registry/rules/sign-before-pack.md +5 -5
  58. package/skills/calimero-rust-sdk/SKILL.md +154 -44
  59. package/skills/calimero-rust-sdk/references/blob-api.md +119 -0
  60. package/skills/calimero-rust-sdk/references/event-handlers.md +122 -0
  61. package/skills/calimero-rust-sdk/references/events.md +2 -1
  62. package/skills/calimero-rust-sdk/references/examples.md +81 -29
  63. package/skills/calimero-rust-sdk/references/migrations.md +123 -0
  64. package/skills/calimero-rust-sdk/references/nested-crdts.md +113 -0
  65. package/skills/calimero-rust-sdk/references/private-storage.md +76 -34
  66. package/skills/calimero-rust-sdk/references/state-collections.md +106 -21
  67. package/skills/calimero-rust-sdk/references/user-and-frozen-storage.md +169 -0
  68. package/skills/calimero-rust-sdk/rules/app-macro-placement.md +5 -2
  69. package/skills/calimero-rust-sdk/rules/no-std-collections.md +5 -2
  70. package/skills/calimero-rust-sdk/rules/state-derives.md +9 -10
  71. package/skills/calimero-rust-sdk/rules/wasm-constraints.md +12 -10
  72. package/skills/calimero-sdk-js/SKILL.md +34 -26
  73. package/skills/calimero-sdk-js/references/build-pipeline.md +6 -6
  74. package/skills/calimero-sdk-js/references/collections.md +11 -11
  75. package/skills/calimero-sdk-js/references/events.md +7 -3
  76. package/skills/calimero-sdk-js/rules/crdt-only-state.md +18 -18
  77. package/skills/calimero-sdk-js/rules/no-console-log.md +6 -6
  78. package/skills/calimero-sdk-js/rules/view-decorator.md +6 -4
@@ -0,0 +1,142 @@
1
+ # WebSocket Events
2
+
3
+ The Calimero node pushes real-time events to subscribers over WebSocket.
4
+
5
+ ## Connect and subscribe
6
+
7
+ ```text
8
+ ws://localhost:2428/ws
9
+ ```
10
+
11
+ After connecting, send:
12
+
13
+ ```json
14
+ { "action": "subscribe", "contextIds": ["<context-id-1>", "<context-id-2>"] }
15
+ ```
16
+
17
+ You can subscribe to multiple context IDs in one message.
18
+
19
+ ---
20
+
21
+ ## Event types
22
+
23
+ | Type | When fired | Payload |
24
+ | ---------------- | ----------------------------------------------------- | ------------------------------ |
25
+ | `ExecutionEvent` | App called `app::emit!()` (Rust) or `env.emit()` (JS) | `{ events: ExecutionEvent[] }` |
26
+ | `StateMutation` | Any context member mutated shared state | `{ newRoot: string }` |
27
+
28
+ ---
29
+
30
+ ## ExecutionEvent schema
31
+
32
+ ```typescript
33
+ interface WsMessage {
34
+ contextId: string;
35
+ type: 'ExecutionEvent';
36
+ data: {
37
+ events: Array<{
38
+ kind: string; // matches Rust enum variant name or JS event name
39
+ data: number[] | object; // byte array (UTF-8 JSON) or plain object
40
+ }>;
41
+ };
42
+ }
43
+ ```
44
+
45
+ `data` is typically a UTF-8 JSON byte array when coming from a Rust app. Decode it:
46
+
47
+ ```typescript
48
+ function decodeEventData(data: unknown): unknown {
49
+ if (Array.isArray(data) && data.every((n) => typeof n === 'number')) {
50
+ try {
51
+ return JSON.parse(new TextDecoder().decode(new Uint8Array(data as number[])));
52
+ } catch {
53
+ return data; // leave as-is if not valid JSON
54
+ }
55
+ }
56
+ return data;
57
+ }
58
+ ```
59
+
60
+ Full consumption pattern:
61
+
62
+ ```typescript
63
+ ws.onmessage = (msg) => {
64
+ const envelope = JSON.parse(msg.data);
65
+ if (envelope.type === 'ExecutionEvent') {
66
+ for (const e of envelope.data.events) {
67
+ const payload = decodeEventData(e.data);
68
+ switch (e.kind) {
69
+ case 'PostCreated':
70
+ handlePostCreated(payload);
71
+ break;
72
+ case 'PostDeleted':
73
+ handlePostDeleted(payload);
74
+ break;
75
+ }
76
+ }
77
+ }
78
+ if (envelope.type === 'StateMutation') {
79
+ // A peer mutated shared state — refetch data if needed
80
+ refreshData();
81
+ }
82
+ };
83
+ ```
84
+
85
+ ---
86
+
87
+ ## StateMutation schema
88
+
89
+ ```typescript
90
+ interface WsMessage {
91
+ contextId: string;
92
+ type: 'StateMutation';
93
+ data: {
94
+ newRoot: string; // hex hash of the new CRDT state root after merge
95
+ };
96
+ }
97
+ ```
98
+
99
+ Use `StateMutation` to know when to refetch state from the node — it fires on every mutation from
100
+ any member, including the local node's own mutations.
101
+
102
+ ---
103
+
104
+ ## Unsubscribe
105
+
106
+ ```json
107
+ { "action": "unsubscribe", "contextIds": ["<context-id>"] }
108
+ ```
109
+
110
+ ---
111
+
112
+ ## Emitting events from Rust
113
+
114
+ ```rust
115
+ #[app::event]
116
+ pub enum Event<'a> {
117
+ PostCreated { title: &'a str },
118
+ PostDeleted { id: u32 },
119
+ }
120
+
121
+ #[app::logic]
122
+ impl AppState {
123
+ pub fn create_post(&mut self, title: String) -> app::Result<()> {
124
+ // ...
125
+ app::emit!(Event::PostCreated { title: &title });
126
+ Ok(())
127
+ }
128
+ }
129
+ ```
130
+
131
+ The `kind` field in the WebSocket message will be `"PostCreated"` or `"PostDeleted"`.
132
+
133
+ ## Emitting events from TypeScript (SDK JS)
134
+
135
+ ```typescript
136
+ import * as env from '@calimero-network/calimero-sdk-js/env';
137
+
138
+ set(key: string, value: string): void {
139
+ this.items.set(key, value);
140
+ env.emit({ kind: 'ItemSet', data: { key, value } });
141
+ }
142
+ ```
@@ -0,0 +1,35 @@
1
+ # Rule: Application ≠ Context
2
+
3
+ **An application is not a context. Installing an app does not create a context.**
4
+
5
+ ## What this means
6
+
7
+ - `meroctl app install` → registers the WASM binary. Returns an `application-id`. No state exists
8
+ yet. No context exists yet.
9
+ - `meroctl context create --application-id <id>` → creates a context. Calls `init()`. State now
10
+ exists. The context has a `context-id`.
11
+
12
+ These are always **two separate commands**. You cannot skip the first step and jump straight to
13
+ calling methods.
14
+
15
+ ## Why this matters
16
+
17
+ When a developer says "install and run my app", the correct sequence is:
18
+
19
+ ```bash
20
+ meroctl app install --path app.wasm # → application-id
21
+ meroctl context create --application-id <id> # → context-id
22
+ meroctl call <context-id> my_method --args '{}'
23
+ ```
24
+
25
+ Not:
26
+
27
+ ```bash
28
+ meroctl app install --path app.wasm
29
+ meroctl call ??? # WRONG — no context yet
30
+ ```
31
+
32
+ ## Analogy
33
+
34
+ Think of an Application as a class definition and a Context as an instance of that class. You can
35
+ have many contexts (instances) from one application (class), each with completely isolated state.
@@ -0,0 +1,55 @@
1
+ # Rule: CRDT Types Only for Shared State
2
+
3
+ **Never use `std::collections` (Rust) or plain `Map`/`Array` (JS/TS) for shared application state.**
4
+
5
+ ## Forbidden for state fields
6
+
7
+ ```rust
8
+ // WRONG — will compile but cause data loss on concurrent writes:
9
+ struct AppState {
10
+ items: std::collections::HashMap<String, String>, // ❌
11
+ log: Vec<String>, // ❌
12
+ seen: std::collections::HashSet<String>, // ❌
13
+ }
14
+ ```
15
+
16
+ ```typescript
17
+ // WRONG — same problem in TypeScript:
18
+ @State
19
+ class AppState {
20
+ items: Map<string, string> = new Map(); // ❌
21
+ log: string[] = []; // ❌
22
+ }
23
+ ```
24
+
25
+ ## Required: use CRDT collections
26
+
27
+ ```rust
28
+ // CORRECT:
29
+ use calimero_storage::collections::{UnorderedMap, Vector, LwwRegister};
30
+
31
+ struct AppState {
32
+ items: UnorderedMap<String, LwwRegister<String>>, // ✓
33
+ log: Vector<String>, // ✓
34
+ }
35
+ ```
36
+
37
+ ```typescript
38
+ // CORRECT:
39
+ import { UnorderedMap, Vector } from '@calimero-network/calimero-sdk-js/collections';
40
+
41
+ @State
42
+ class AppState {
43
+ items: UnorderedMap<string, string> = new UnorderedMap(); // ✓
44
+ log: Vector<string> = new Vector(); // ✓
45
+ }
46
+ ```
47
+
48
+ ## Why
49
+
50
+ Standard collections have no merge semantics. When two context members both write to a `HashMap`
51
+ while offline, merging their states is undefined — the CRDT engine cannot resolve conflicts
52
+ automatically and data will be silently lost or overwritten.
53
+
54
+ CRDT collections are designed for exactly this scenario: they guarantee that all members converge to
55
+ the same state regardless of write order or network partitions.
@@ -1,30 +1,30 @@
1
1
  # calimero-desktop — Agent Instructions
2
2
 
3
- You are helping a developer integrate their app frontend with **Calimero Desktop SSO** —
4
- the flow where users open an app from the Desktop and are automatically logged in without
5
- a manual auth screen.
3
+ You are helping a developer integrate their app frontend with **Calimero Desktop SSO** — the flow
4
+ where users open an app from the Desktop and are automatically logged in without a manual auth
5
+ screen.
6
6
 
7
- > **NOT this skill** if the developer just needs to authenticate manually via the login
8
- > form — that is handled entirely by `calimero-client-js`. This skill is specifically for
9
- > reading SSO tokens that Calimero Desktop passes in the URL hash.
7
+ > **NOT this skill** if the developer just needs to authenticate manually via the login form — that
8
+ > is handled entirely by `calimero-client-js`. This skill is specifically for reading SSO tokens
9
+ > that Calimero Desktop passes in the URL hash.
10
10
 
11
11
  ## What Desktop does
12
12
 
13
- Calimero Desktop opens app frontends in a browser window and passes auth tokens via the
14
- URL hash — so users are automatically logged in without going through the manual auth flow.
13
+ Calimero Desktop opens app frontends in a browser window and passes auth tokens via the URL hash —
14
+ so users are automatically logged in without going through the manual auth flow.
15
15
 
16
16
  ## Hash parameters passed by Desktop
17
17
 
18
- | Parameter | Type | Description |
19
- | --- | --- | --- |
20
- | `access_token` | string | JWT for authenticated node calls |
21
- | `refresh_token` | string | Token to obtain a new access token |
22
- | `node_url` | string | URL of the local node (`http://localhost:PORT`) |
23
- | `application_id` | string | The installed app's ID on this node |
18
+ | Parameter | Type | Description |
19
+ | ---------------- | ------ | ----------------------------------------------- |
20
+ | `access_token` | string | JWT for authenticated node calls |
21
+ | `refresh_token` | string | Token to obtain a new access token |
22
+ | `node_url` | string | URL of the local node (`http://localhost:PORT`) |
23
+ | `application_id` | string | The installed app's ID on this node |
24
24
 
25
25
  ## Example URL
26
26
 
27
- ```
27
+ ```text
28
28
  https://your-app.com/#access_token=eyJ...&refresh_token=eyJ...&node_url=http://localhost:2428&application_id=abc123
29
29
  ```
30
30
 
@@ -44,10 +44,15 @@ if (accessToken && nodeUrl) {
44
44
 
45
45
  ## Critical rule
46
46
 
47
- Always fall back to manual login when hash params are absent. The app must work when
48
- opened in a regular browser, not only from Desktop.
47
+ Always fall back to manual login when hash params are absent. The app must work when opened in a
48
+ regular browser, not only from Desktop.
49
+
50
+ ## Related skills
51
+
52
+ - **`calimero-client-js`** — mero-js / mero-react API for auth, RPC, and WebSocket subscriptions
53
+ - **`calimero-core`** — auth flow (JWT tokens, login, refresh) and JSON-RPC protocol detail
49
54
 
50
55
  ## References
51
56
 
52
- See `references/` for full integration pattern and how Desktop discovers app URLs.
53
- See `rules/` for the fallback requirement.
57
+ See `references/` for full integration pattern and how Desktop discovers app URLs. See `rules/` for
58
+ the fallback requirement.
@@ -72,8 +72,8 @@ document.addEventListener('DOMContentLoaded', bootstrap);
72
72
 
73
73
  ## How Desktop discovers your app's frontend URL
74
74
 
75
- Desktop reads the `links.frontend` field from your app's `manifest.json` inside the
76
- installed bundle. Set this field so Desktop can open your app:
75
+ Desktop reads the `links.frontend` field from your app's `manifest.json` inside the installed
76
+ bundle. Set this field so Desktop can open your app:
77
77
 
78
78
  ```json
79
79
  {
@@ -1,6 +1,7 @@
1
1
  # Rule: Always fall back to manual login if hash params are absent
2
2
 
3
3
  Your app must work in two scenarios:
4
+
4
5
  1. Opened from Calimero Desktop — SSO hash params present
5
6
  2. Opened directly in a browser — no hash params
6
7
 
@@ -29,5 +30,5 @@ if (token) {
29
30
 
30
31
  ## Why
31
32
 
32
- Desktop is not the only way users access apps. Developers test in browsers directly.
33
- Organizations may embed apps in other contexts. The app must be self-sufficient.
33
+ Desktop is not the only way users access apps. Developers test in browsers directly. Organizations
34
+ may embed apps in other contexts. The app must be self-sufficient.
@@ -1,54 +1,281 @@
1
1
  # calimero-merobox — Agent Instructions
2
2
 
3
- You are helping a developer set up a **local multi-node Calimero network** using Merobox.
3
+ You are helping a developer set up and test a **local Calimero network** using Merobox.
4
4
 
5
5
  ## What Merobox is
6
6
 
7
- Merobox is a Docker-based toolchain for running Calimero nodes locally. It handles:
8
- - Spinning up multiple nodes in Docker containers
9
- - Bootstrapping identities and contexts
10
- - Defining reusable network topologies via workflow YAML files
11
- - CI/CD integration
7
+ Merobox is a Python CLI tool for running Calimero nodes in Docker containers. It handles:
8
+
9
+ - Starting/stopping nodes with `merobox run` / `merobox stop`
10
+ - App installation and method execution
11
+ - Identity and context management
12
+ - Automated multi-step test workflows via YAML (`merobox bootstrap run`)
13
+ - Multi-node orchestration for integration testing
12
14
 
13
15
  ## Install
14
16
 
15
17
  ```bash
16
- # Using pipx (recommended)
18
+ # macOS
19
+ brew install merobox
20
+
21
+ # Ubuntu/Debian
22
+ curl -fsSL https://calimero-network.github.io/merobox/gpg.key \
23
+ | sudo tee /usr/share/keyrings/merobox.gpg > /dev/null
24
+ echo "deb [signed-by=/usr/share/keyrings/merobox.gpg] https://calimero-network.github.io/merobox stable main" \
25
+ | sudo tee /etc/apt/sources.list.d/merobox.list
26
+ sudo apt update && sudo apt install merobox
27
+
28
+ # pipx (any platform)
17
29
  pipx install merobox
18
30
 
19
- # macOS with Homebrew
20
- brew install calimero-network/tap/merobox
31
+ merobox --version # verify
32
+ ```
33
+
34
+ Requires Docker 20.10+ running.
35
+
36
+ ## Node management
37
+
38
+ ```bash
39
+ # Start a node
40
+ merobox run --name my-node
41
+
42
+ # Start with custom ports
43
+ merobox run --name my-node --server-port 2428 --swarm-port 2528
44
+
45
+ # List running nodes
46
+ merobox list
47
+
48
+ # Check node health
49
+ merobox health my-node
50
+
51
+ # View logs
52
+ merobox logs my-node
53
+ merobox logs my-node --follow # follow in real-time
21
54
 
22
- # Requires: Docker 20.10+ running
55
+ # Stop a node
56
+ merobox stop my-node
57
+
58
+ # Delete all node data (destructive)
59
+ merobox nuke my-node
23
60
  ```
24
61
 
25
- ## Quick start
62
+ ## App and context management
26
63
 
27
64
  ```bash
28
- # Create a workflow file
29
- cat > workflow.yml << EOF
65
+ # Install a WASM app on a node
66
+ merobox install --node my-node --path ./app.wasm --dev
67
+
68
+ # List installed apps
69
+ merobox application list --node my-node
70
+
71
+ # Create a context
72
+ merobox context create --node my-node --application-id <app-id>
73
+
74
+ # List contexts
75
+ merobox context list --node my-node
76
+
77
+ # Call a method
78
+ merobox call my-node <context-id> <method> '{"key":"hello","value":"world"}'
79
+ ```
80
+
81
+ ## Identity management
82
+
83
+ ```bash
84
+ merobox identity generate --node my-node
85
+ ```
86
+
87
+ ## Blob storage
88
+
89
+ ```bash
90
+ merobox blob upload --node my-node --file ./data.txt
91
+ merobox blob list-blobs --node my-node
92
+ merobox blob download --node my-node --blob-id <id> --output ./out.txt
93
+ merobox blob delete --node my-node --blob-id <id> --yes
94
+ ```
95
+
96
+ ## Workflow automation (bootstrap)
97
+
98
+ Workflows are the most powerful feature — YAML files that orchestrate multi-step scenarios.
99
+
100
+ ```bash
101
+ merobox bootstrap run workflow.yml # execute
102
+ merobox bootstrap validate workflow.yml # validate only
103
+ merobox bootstrap create-sample # scaffold example
104
+ ```
105
+
106
+ ### Minimal workflow example
107
+
108
+ ```yaml
109
+ name: KV Store Test
110
+ steps:
111
+ - type: install_application
112
+ node: node-1
113
+ path: ./kv_store.wasm
114
+ outputs:
115
+ app_id: 'application_id'
116
+
117
+ - type: create_context
118
+ node: node-1
119
+ application_id: '{{app_id}}'
120
+ outputs:
121
+ ctx_id: 'context.context_id'
122
+
123
+ - type: create_identity
124
+ node: node-1
125
+ outputs:
126
+ pub: 'public_key'
127
+
128
+ - type: call
129
+ node: node-1
130
+ context_id: '{{ctx_id}}'
131
+ method: set
132
+ args:
133
+ key: 'hello'
134
+ value: 'world'
135
+ executor_public_key: '{{pub}}'
136
+ outputs:
137
+ result: 'output'
138
+
139
+ - type: assert
140
+ statements:
141
+ - 'is_set({{result}})'
142
+ ```
143
+
144
+ ### Available step types
145
+
146
+ | Step | What it does |
147
+ | --------------------- | ------------------------------------------------------- |
148
+ | `install_application` | Install WASM app, capture `application_id` |
149
+ | `create_context` | Create context, capture `context_id` and `seed` |
150
+ | `create_identity` | Create identity, capture `private_key` and `public_key` |
151
+ | `join_context` | Join a node to a context (targeted invitation) |
152
+ | `invite_open` | Create open invitation (anyone can join) |
153
+ | `join_open` | Join via open invitation |
154
+ | `call` | Execute app method, capture output |
155
+ | `wait` | Sleep N seconds |
156
+ | `repeat` | Loop with index variable |
157
+ | `assert` | Validate values (`is_set`, `contains`, `==`) |
158
+ | `json_assert` | JSON equality/subset checks |
159
+ | `upload_blob` | Upload file to blob storage, capture `blob_id` |
160
+ | `script` | Run a shell script |
161
+ | `fuzzy_test` | Randomized load test (30-60+ min) |
162
+
163
+ ### Multi-node example
164
+
165
+ ```yaml
166
+ name: Two-Node Sync Test
167
+ steps:
168
+ - type: install_application
169
+ node: node-1
170
+ path: ./app.wasm
171
+ outputs:
172
+ app_id: 'application_id'
173
+
174
+ - type: create_context
175
+ node: node-1
176
+ application_id: '{{app_id}}'
177
+ outputs:
178
+ ctx: 'context.context_id'
179
+
180
+ - type: create_identity
181
+ node: node-1
182
+ outputs:
183
+ pub1: 'public_key'
184
+
185
+ - type: create_identity
186
+ node: node-2
187
+ outputs:
188
+ pub2: 'public_key'
189
+
190
+ # invite node-2 to join the context
191
+ - type: invite_open
192
+ node: node-1
193
+ context_id: '{{ctx}}'
194
+ granter_id: '{{pub1}}'
195
+ outputs:
196
+ invite: 'invitation'
197
+
198
+ - type: join_open
199
+ node: node-2
200
+ invitee_id: '{{pub2}}'
201
+ invitation: '{{invite}}'
202
+
203
+ - type: wait
204
+ seconds: 2 # allow sync
205
+
206
+ - type: call
207
+ node: node-1
208
+ context_id: '{{ctx}}'
209
+ method: set
210
+ args:
211
+ key: 'msg'
212
+ value: 'hello'
213
+ executor_public_key: '{{pub1}}'
214
+
215
+ - type: wait
216
+ seconds: 1
217
+
218
+ - type: call
219
+ node: node-2
220
+ context_id: '{{ctx}}'
221
+ method: get
222
+ args:
223
+ key: 'msg'
224
+ executor_public_key: '{{pub2}}'
225
+ outputs:
226
+ val: 'output'
227
+
228
+ - type: assert
229
+ statements:
230
+ - "contains({{val}}, 'hello')"
231
+ ```
232
+
233
+ ### Auth service (production-like setup)
234
+
235
+ ```yaml
236
+ name: Workflow with Auth
237
+ auth_service: true # enables Traefik + auth middleware
238
+
30
239
  nodes:
31
- - name: node1
32
- port: 2428
33
- - name: node2
34
- port: 2429
35
- EOF
240
+ count: 1
241
+ prefix: 'calimero-node'
242
+ image: 'ghcr.io/calimero-network/merod:edge'
243
+
244
+ steps:
245
+ - type: wait
246
+ seconds: 5
247
+ message: 'Waiting for auth service...'
248
+ ```
36
249
 
37
- # Start the network
38
- merobox up --workflow workflow.yml
250
+ Node URL with auth: `http://node1.127.0.0.1.nip.io`
39
251
 
40
- # Stop it
41
- merobox down
252
+ ## Variable substitution
253
+
254
+ ```yaml
255
+ {{variable_name}} # step output
256
+ {{env.MY_VAR}} # environment variable
257
+ {{iteration}} # loop index in repeat
258
+ {{random_int(1, 100)}} # random integer
259
+ {{random_string(8)}} # random string
260
+ {{uuid}} # UUID v4
261
+ {{timestamp}} # Unix timestamp
262
+ {{random_node}} # random node from list
42
263
  ```
43
264
 
44
265
  ## When to use Merobox vs meroctl
45
266
 
46
- | Task | Use |
47
- | --- | --- |
48
- | Local multi-node dev and testing | Merobox |
49
- | Managing a single production node | meroctl |
50
- | CI pipeline with multi-node scenarios | Merobox |
51
- | One-off command against a live node | meroctl |
267
+ | Task | Use |
268
+ | ---------------------------------------- | ------- |
269
+ | Local multi-node dev and testing | Merobox |
270
+ | CI pipeline with multi-step scenarios | Merobox |
271
+ | Quick single command against a live node | meroctl |
272
+ | Managing a production node | meroctl |
273
+
274
+ ## Related skills
275
+
276
+ - **`calimero-merod`** — `merod` daemon setup (init, ports, health endpoint)
277
+ - **`calimero-meroctl`** — full `meroctl` CLI reference for scripting against a live node
278
+ - **`calimero-core`** — context/app model and namespace/group participation model
52
279
 
53
280
  ## References
54
281
 
@@ -1,6 +1,7 @@
1
1
  # CI Integration
2
2
 
3
- Use Merobox in GitHub Actions or other CI systems to run integration tests against a real multi-node network.
3
+ Use Merobox in GitHub Actions or other CI systems to run integration tests against a real multi-node
4
+ network.
4
5
 
5
6
  ## GitHub Actions example
6
7
 
@@ -49,4 +50,4 @@ jobs:
49
50
  ## Reference workflow files
50
51
 
51
52
  The Battleships repo has well-structured workflow examples:
52
- https://github.com/calimero-network/battleships/tree/main/workflows
53
+ <https://github.com/calimero-network/battleships/tree/main/workflows>
@@ -1,6 +1,7 @@
1
1
  # Merobox Workflow Files
2
2
 
3
- Workflow files define reusable network topologies. Commit them to your repo for repeatable local dev and CI environments.
3
+ Workflow files define reusable network topologies. Commit them to your repo for repeatable local dev
4
+ and CI environments.
4
5
 
5
6
  ## Minimal workflow
6
7
 
@@ -30,16 +31,16 @@ setup:
30
31
 
31
32
  - step: create_context
32
33
  node: node1
33
- app_id: "{{ install_app.app_id }}"
34
+ app_id: '{{ install_app.app_id }}'
34
35
 
35
36
  - step: invite_member
36
37
  node: node1
37
- context_id: "{{ create_context.context_id }}"
38
- identity: "{{ node2.identity }}"
38
+ context_id: '{{ create_context.context_id }}'
39
+ identity: '{{ node2.identity }}'
39
40
 
40
41
  - step: join_context
41
42
  node: node2
42
- invitation: "{{ invite_member.invitation }}"
43
+ invitation: '{{ invite_member.invitation }}'
43
44
  ```
44
45
 
45
46
  ## Commands
@@ -64,6 +65,7 @@ merobox down --purge
64
65
  ## Accessing nodes after startup
65
66
 
66
67
  Each node exposes its JSON-RPC at the configured port:
68
+
67
69
  - `node1` → `http://localhost:2428`
68
70
  - `node2` → `http://localhost:2429`
69
71