@3sixty/node 0.0.0-stage → 0.20.0-next.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 (47) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +34 -2
  3. package/bin/index.js +4 -0
  4. package/dist/locales/en.json +18011 -0
  5. package/docs/changelog.md +0 -0
  6. package/docs/commands/bootstrap-dev.md +51 -0
  7. package/docs/commands/help.md +61 -0
  8. package/docs/commands/identity-create.md +67 -0
  9. package/docs/commands/identity-import.md +37 -0
  10. package/docs/commands/identity-list.md +31 -0
  11. package/docs/commands/identity-remove.md +46 -0
  12. package/docs/commands/identity-resolve.md +38 -0
  13. package/docs/commands/identity-verifiable-credential-create.md +58 -0
  14. package/docs/commands/identity-verification-method-create.md +55 -0
  15. package/docs/commands/identity-verification-method-import.md +46 -0
  16. package/docs/commands/node-identity-get.md +31 -0
  17. package/docs/commands/node-identity-set.md +36 -0
  18. package/docs/commands/node-org-id-get.md +32 -0
  19. package/docs/commands/node-org-id-set.md +37 -0
  20. package/docs/commands/org-users-list.md +40 -0
  21. package/docs/commands/tenant-create.md +55 -0
  22. package/docs/commands/tenant-get.md +34 -0
  23. package/docs/commands/tenant-import.md +46 -0
  24. package/docs/commands/tenant-list-by-org.md +34 -0
  25. package/docs/commands/tenant-list.md +31 -0
  26. package/docs/commands/tenant-org-alias-remove.md +39 -0
  27. package/docs/commands/tenant-org-id-set.md +39 -0
  28. package/docs/commands/tenant-remove.md +34 -0
  29. package/docs/commands/tenant-update.md +46 -0
  30. package/docs/commands/user-create.md +73 -0
  31. package/docs/commands/user-get.md +43 -0
  32. package/docs/commands/user-remove.md +43 -0
  33. package/docs/commands/user-update-password.md +58 -0
  34. package/docs/commands/user-update.md +58 -0
  35. package/docs/commands/vault-key-create.md +58 -0
  36. package/docs/commands/vault-key-import.md +43 -0
  37. package/docs/commands/vault-key-remove.md +37 -0
  38. package/docs/commands/vault-key-update.md +40 -0
  39. package/docs/configuration.md +234 -0
  40. package/docs/deployment-docker.md +142 -0
  41. package/docs/deployment-ec2.md +193 -0
  42. package/docs/deployment.md +4 -0
  43. package/docs/open-api/spec.json +24802 -0
  44. package/docs/usage.md +239 -0
  45. package/locales/en.json +1 -0
  46. package/package.json +39 -5
  47. package/src/index.js +12 -0
@@ -0,0 +1,58 @@
1
+ # vault-key-create
2
+
3
+ Create a vault key for an identity.
4
+
5
+ ## Tenancy
6
+
7
+ Available in single-tenant and multi-tenant mode, with the same behaviour in both.
8
+
9
+ ## Usage
10
+
11
+ ```text
12
+ vault-key-create: Create a vault key for an identity
13
+
14
+ env-prefix: (string, optional)
15
+ Prefix to use for standard .env files e.g. TWIN_.
16
+
17
+ identity: (string, did, required)
18
+ The DID to associate the key with.
19
+
20
+ key-id: (string, required)
21
+ The ID of the key.
22
+
23
+ key-type: (string, default: 'Ed25519', optional, options: [Ed25519, ChaCha20Poly1305])
24
+ The type of the key.
25
+
26
+ overwrite-mode: (string, default: 'skip', optional, options: [skip, overwrite, error])
27
+ The mode to use when a key with the same id already exists.
28
+
29
+ load-env: (string, optional)
30
+ Comma separated list of paths to .env files to read input parameters from.
31
+
32
+ output-json: (string, optional)
33
+ Path to a .json file to store the command output.
34
+
35
+ output-env: (string, optional)
36
+ Path to a .env file to store the command output.
37
+
38
+ output-env-prefix: (string, optional)
39
+ Prefix to use for variables in the output .env file.
40
+
41
+ Example: vault-key-create --identity="did:iota:...." --key-id="my-key" --key-type="Ed25519"
42
+ ```
43
+
44
+ ## Examples
45
+
46
+ Create the authentication signing key for the node:
47
+
48
+ ```shell
49
+ 3sixty-node vault-key-create --load-env="node-identity.env" --identity=!NODE_DID --key-type=Ed25519 --key-id=!TWIN_AUTH_SIGNING_KEY_ID --overwrite-mode=skip --output-json="node-auth-key.json" --output-env="node-auth-key.env"
50
+ ```
51
+
52
+ Create a blob storage encryption key for the organisation:
53
+
54
+ ```shell
55
+ 3sixty-node vault-key-create --load-env="organization-identity.env" --identity=!ORGANIZATION_DID --key-type=ChaCha20Poly1305 --key-id=!TWIN_BLOB_STORAGE_ENCRYPTION_KEY_ID --overwrite-mode=skip --output-json="organization-blob-encryption.json"
56
+ ```
57
+
58
+ See the [usage guide](../usage.md) for running the node and the full list of commands.
@@ -0,0 +1,43 @@
1
+ # vault-key-import
2
+
3
+ Import a vault key for an identity.
4
+
5
+ ## Tenancy
6
+
7
+ Available in single-tenant and multi-tenant mode, with the same behaviour in both.
8
+
9
+ ## Usage
10
+
11
+ ```text
12
+ vault-key-import: Import a vault key for an identity
13
+
14
+ env-prefix: (string, optional)
15
+ Prefix to use for standard .env files e.g. TWIN_.
16
+
17
+ identity: (string, did, required)
18
+ The DID to associate the key with.
19
+
20
+ key-id: (string, required)
21
+ The ID of the key.
22
+
23
+ key-type: (string, default: 'Ed25519', optional, options: [Ed25519, ChaCha20Poly1305])
24
+ The type of the key.
25
+
26
+ private-key-hex: (string, required)
27
+ The private key in hex format.
28
+
29
+ load-env: (string, optional)
30
+ Comma separated list of paths to .env files to read input parameters from.
31
+
32
+ Example: vault-key-import --identity="did:iota:...." --key-id="my-key" --key-type=Ed25519 --private-key-hex="15...a2"
33
+ ```
34
+
35
+ ## Examples
36
+
37
+ Import an existing authentication signing key:
38
+
39
+ ```shell
40
+ 3sixty-node vault-key-import --load-env="node-identity.env,node-auth-key.env" --identity=!NODE_DID --key-id=!TWIN_AUTH_SIGNING_KEY_ID --key-type=!KEY_TYPE --private-key-hex=!PRIVATE_KEY_HEX
41
+ ```
42
+
43
+ See the [usage guide](../usage.md) for running the node and the full list of commands.
@@ -0,0 +1,37 @@
1
+ # vault-key-remove
2
+
3
+ Remove a vault key for an identity.
4
+
5
+ ## Tenancy
6
+
7
+ Available in single-tenant and multi-tenant mode, with the same behaviour in both.
8
+
9
+ ## Usage
10
+
11
+ ```text
12
+ vault-key-remove: Remove a vault key for an identity
13
+
14
+ env-prefix: (string, optional)
15
+ Prefix to use for standard .env files e.g. TWIN_.
16
+
17
+ identity: (string, did, required)
18
+ The DID the key is associated with.
19
+
20
+ key-id: (string, required)
21
+ The ID of the key.
22
+
23
+ load-env: (string, optional)
24
+ Comma separated list of paths to .env files to read input parameters from.
25
+
26
+ Example: vault-key-remove --identity="did:iota:...." --key-id="my-key"
27
+ ```
28
+
29
+ ## Examples
30
+
31
+ Remove a vault key:
32
+
33
+ ```shell
34
+ 3sixty-node vault-key-remove --load-env="node-identity.env" --identity=!NODE_DID --key-id="my-key"
35
+ ```
36
+
37
+ See the [usage guide](../usage.md) for running the node and the full list of commands.
@@ -0,0 +1,40 @@
1
+ # vault-key-update
2
+
3
+ Replace the key material of an existing vault key for an identity.
4
+
5
+ ## Tenancy
6
+
7
+ Available in single-tenant and multi-tenant mode, with the same behaviour in both. The key keeps its existing type, only the key material is replaced.
8
+
9
+ ## Usage
10
+
11
+ ```text
12
+ vault-key-update: Replace the key material of an existing vault key for an identity
13
+
14
+ env-prefix: (string, optional)
15
+ Prefix to use for standard .env files e.g. TWIN_.
16
+
17
+ identity: (string, did, required)
18
+ The DID the key is associated with.
19
+
20
+ key-id: (string, required)
21
+ The ID of the key.
22
+
23
+ private-key-hex: (string, required)
24
+ The replacement private key in hex format, the key type is kept.
25
+
26
+ load-env: (string, optional)
27
+ Comma separated list of paths to .env files to read input parameters from.
28
+
29
+ Example: vault-key-update --identity="did:iota:...." --key-id="my-key" --private-key-hex="15...a2"
30
+ ```
31
+
32
+ ## Examples
33
+
34
+ Replace the material of an existing key:
35
+
36
+ ```shell
37
+ 3sixty-node vault-key-update --load-env="node-identity.env" --identity=!NODE_DID --key-id="my-key" --private-key-hex="0x..."
38
+ ```
39
+
40
+ See the [usage guide](../usage.md) for running the node and the full list of commands.
@@ -0,0 +1,234 @@
1
+ # Node Configuration
2
+
3
+ ## Available Example Configurations
4
+
5
+ Four ready-to-use example files are provided in `apps/node/`. Copy the relevant file to `.env` as a starting point:
6
+
7
+ | File | Role |
8
+ | ----------------------------------- | ------------------------------------------------------------------------- |
9
+ | `.env.example-local-entity-storage` | Full-featured node using local file/entity storage |
10
+ | `.env.example-aws-ec2` | Full-featured node deployed on Amazon AWS EC2 with DynamoDB |
11
+ | `.env.example-iota-testnet` | Full-featured node connected to the IOTA testnet |
12
+ | `.env.example-federated-catalogue` | Standalone federated catalogue server (no dataspace or rights management) |
13
+
14
+ ## Extension Loading
15
+
16
+ The 3Sixty Node supports loading extensions dynamically to extend its functionality. Extensions can be loaded from multiple sources using protocol-based syntax.
17
+
18
+ ### Extension Loading Syntax
19
+
20
+ Extensions are configured via the `TWIN_EXTENSIONS` environment variable. Multiple extensions can be specified as a comma-separated list.
21
+
22
+ #### Local File Extensions
23
+
24
+ Load extensions from the local filesystem:
25
+
26
+ ```bash
27
+ # Relative path
28
+ TWIN_EXTENSIONS="./my-extension.mjs"
29
+
30
+ # Absolute path
31
+ TWIN_EXTENSIONS="/path/to/extension.mjs"
32
+ ```
33
+
34
+ #### NPM Package Extensions
35
+
36
+ Load extensions from npm packages. The package will be automatically downloaded and installed if not already cached:
37
+
38
+ ```bash
39
+ # Basic npm package
40
+ TWIN_EXTENSIONS="npm:@3sixty/identity-management-service"
41
+
42
+ # Scoped package
43
+ TWIN_EXTENSIONS="npm:@org/custom-extension"
44
+ ```
45
+
46
+ **Note:** Version pinning (e.g., `npm:@3sixty/pkg@1.2.3`) is planned for a future release. Currently, the latest version is installed.
47
+
48
+ #### HTTPS URL Extensions
49
+
50
+ Load extensions from remote HTTPS URLs:
51
+
52
+ ```bash
53
+ TWIN_EXTENSIONS="https://example.twin.org/extensions/my-module.mjs"
54
+ ```
55
+
56
+ **⚠️ SECURITY WARNING:** Loading code from HTTPS URLs introduces security risks. Only load extensions from trusted sources that you control or verify.
57
+
58
+ Security features:
59
+
60
+ - Only HTTPS is allowed (HTTP is rejected)
61
+ - Default 10 MB size limit to prevent DoS attacks
62
+ - Downloaded modules are cached locally
63
+ - Security warning is displayed when loading
64
+
65
+ #### Multiple Extensions
66
+
67
+ Load multiple extensions from different sources:
68
+
69
+ ```bash
70
+ TWIN_EXTENSIONS="@3sixty/identity-management-service,npm:@3sixty/custom-service,./local-extension.mjs,https://example.com/remote.mjs"
71
+ ```
72
+
73
+ ### Extension Configuration Options
74
+
75
+ #### Maximum Download Size
76
+
77
+ Configure the maximum size (in MB) for HTTPS extension downloads:
78
+
79
+ ```bash
80
+ TWIN_EXTENSIONS_MAX_SIZE_MB=10 # Default is 10 MB
81
+ ```
82
+
83
+ #### Cache Management
84
+
85
+ By default, downloaded extensions are cached in `.tmp/extensions/` directory to speed up subsequent startups. To clear the cache on each startup:
86
+
87
+ ```bash
88
+ TWIN_EXTENSIONS_CLEAR_CACHE=true # Default is false
89
+ ```
90
+
91
+ #### Custom Cache Directory
92
+
93
+ You can configure a custom directory for extension caching:
94
+
95
+ ```bash
96
+ TWIN_EXTENSIONS_CACHE_DIRECTORY="cache" # Default is ".tmp"
97
+ ```
98
+
99
+ This allows you to:
100
+
101
+ - Use a dedicated partition with more space
102
+ - Separate cache by environment (dev/staging/prod)
103
+ - Follow corporate storage policies
104
+ - Create persistent cache directories for better performance
105
+
106
+ ### Extension API
107
+
108
+ Extensions must export specific lifecycle methods to integrate with the node:
109
+
110
+ #### `extensionInitialise(envVars, nodeEngineConfig)`
111
+
112
+ Called during configuration phase. Allows the extension to modify environment variables and engine configuration.
113
+
114
+ ```typescript
115
+ export async function extensionInitialise(
116
+ envVars: INodeEnvironmentVariables,
117
+ nodeEngineConfig: INodeEngineConfig
118
+ ): Promise<void> {
119
+ // Modify configuration here
120
+ }
121
+ ```
122
+
123
+ #### `extensionInitialiseEngine(engineCore)`
124
+
125
+ Called after the engine is created but before the server starts.
126
+
127
+ ```typescript
128
+ export async function extensionInitialiseEngine(engineCore: IEngineCore): Promise<void> {
129
+ // Register components, set up services, etc.
130
+ }
131
+ ```
132
+
133
+ #### `extensionInitialiseEngineServer(engineCore, engineServer)`
134
+
135
+ Called after the engine server is created but before it starts listening.
136
+
137
+ ```typescript
138
+ export async function extensionInitialiseEngineServer(
139
+ engineCore: IEngineCore,
140
+ engineServer: IEngineServer
141
+ ): Promise<void> {
142
+ // Add routes, configure server, etc.
143
+ }
144
+ ```
145
+
146
+ #### `extensionShutdown()`
147
+
148
+ Called when the node is shutting down. Clean up resources here.
149
+
150
+ ```typescript
151
+ export async function extensionShutdown(): Promise<void> {
152
+ // Clean up resources
153
+ }
154
+ ```
155
+
156
+ ### Example Extension
157
+
158
+ ```typescript
159
+ // my-extension.mjs
160
+ export async function extensionInitialise(envVars, nodeEngineConfig) {
161
+ console.log('Extension initializing with config');
162
+ }
163
+
164
+ export async function extensionInitialiseEngine(engineCore) {
165
+ engineCore.logInfo('Custom extension loaded');
166
+ }
167
+
168
+ export async function extensionInitialiseEngineServer(engineCore, engineServer) {
169
+ // Add custom routes
170
+ engineServer.addRestRoute({
171
+ path: '/custom/endpoint',
172
+ method: 'GET',
173
+ handler: async () => ({ body: { message: 'Hello from extension!' } })
174
+ });
175
+ }
176
+
177
+ export async function extensionShutdown() {
178
+ console.log('Extension shutting down');
179
+ }
180
+ ```
181
+
182
+ ### Cache Directory Structure
183
+
184
+ Downloaded extensions are stored in:
185
+
186
+ ```text
187
+ <execution-directory>/<cache-directory>/extensions/
188
+ ├── npm/ # NPM packages
189
+ │ └── node_modules/
190
+ │ └── @3sixty/...
191
+ └── https/ # HTTPS downloads
192
+ └── <hashed-filename>.mjs
193
+ ```
194
+
195
+ Where `<cache-directory>` is:
196
+
197
+ - `.tmp` by default
198
+ - Configurable via `TWIN_EXTENSIONS_CACHE_DIRECTORY`
199
+
200
+ Examples:
201
+
202
+ - Default: `./.tmp/extensions/`
203
+ - Custom: `./cache/extensions/` (if `TWIN_EXTENSIONS_CACHE_DIRECTORY="cache"`)
204
+ - Absolute: `/var/cache/twin-node/extensions/` (if `TWIN_EXTENSIONS_CACHE_DIRECTORY="/var/cache/twin-node"`)
205
+
206
+ ### Security Best Practices
207
+
208
+ 1. **Prefer npm: protocol** over https: for third-party extensions (npm has package verification and malware scanning)
209
+ 2. **Use local files** (`./extension.mjs`) for maximum security and control
210
+ 3. **Verify HTTPS sources** before using - only load from domains you trust
211
+ 4. **Review extension code** before deployment, especially for HTTPS sources
212
+ 5. **Monitor cache directory** - review what's been downloaded to `.tmp/extensions/`
213
+ 6. **Use environment-specific configuration** - different extensions for dev/staging/prod
214
+
215
+ ### Troubleshooting
216
+
217
+ #### Extension fails to load
218
+
219
+ - Check that npm is installed and accessible in PATH (required for npm: protocol)
220
+ - Verify the extension file/URL is accessible
221
+ - Check `.tmp/extensions/` cache for partially downloaded files
222
+ - Review node logs for specific error messages
223
+
224
+ #### Clear extension cache manually
225
+
226
+ ```bash
227
+ rm -rf .tmp/extensions
228
+ ```
229
+
230
+ #### NPM package not found
231
+
232
+ - Verify the package name is correct
233
+ - Check network connectivity
234
+ - Ensure npm registry is accessible
@@ -0,0 +1,142 @@
1
+ # Node Deployment Docker
2
+
3
+ The 3Sixty Node can be run as a Docker container, either from the published image or from an image you build yourself.
4
+
5
+ ## Published Image
6
+
7
+ Images are published to GitHub Packages as [ghcr.io/3sixtyglobal/node](https://github.com/3sixtyglobal/node/pkgs/container/3sixty-node) for `linux/amd64` and `linux/arm64`, with the following tags:
8
+
9
+ | Tag | Description |
10
+ | --------- | ------------------------------------------------ |
11
+ | `latest` | The most recent production release. |
12
+ | `next` | The most recent prerelease. |
13
+ | `<x.y.z>` | A specific version, for example `0.10.1-next.6`. |
14
+
15
+ ```shell
16
+ docker pull ghcr.io/3sixtyglobal/node:latest
17
+ ```
18
+
19
+ ## Building the Image
20
+
21
+ The dockerfile is in `apps/node/deploy/dockerfile`. It builds `@3sixty/node-core` from the repository source and packs it, then installs that package and the other dependencies with pnpm, merges the translation messages, and removes the development dependencies. The repository root forms the build context, and `apps/node/deploy/dockerfile.dockerignore` keeps your local `node_modules`, build output and `.env` files out of the image.
22
+
23
+ Build the image from the repository root:
24
+
25
+ ```shell
26
+ docker build -t 3sixty-node -f apps/node/deploy/dockerfile . --load
27
+ ```
28
+
29
+ > **Note**: The `--load` flag is required when using Docker Buildx to ensure the image is loaded into your local Docker registry. Without it, the image will only exist in the build cache.
30
+
31
+ To build for both of the published platforms use a Buildx builder with the `docker-container` driver:
32
+
33
+ ```shell
34
+ docker buildx create --name 3sixty-multiplatform-builder --driver docker-container --use
35
+ docker buildx build --platform linux/amd64,linux/arm64 -t 3sixty-node -f apps/node/deploy/dockerfile .
36
+ ```
37
+
38
+ The examples below use the locally built `3sixty-node` image, replace it with `ghcr.io/3sixtyglobal/node:<tag>` to use the published image.
39
+
40
+ ## Configuration
41
+
42
+ The node reads its configuration from `/app/.env` in the container, and from environment variables, which take precedence over the file. The image does not contain a `.env`, so start from one of the example configurations described in [Node Configuration](./configuration.md) and mount it into the container:
43
+
44
+ ```shell
45
+ -v /home/twin-node/.env:/app/.env:ro
46
+ ```
47
+
48
+ Mount the file rather than passing it with `--env-file`. Docker does not remove the quotes around values in an env file, so the quoted values used in the example configurations would be read incorrectly.
49
+
50
+ The image sets the following environment variables, which override the same settings in `/app/.env`:
51
+
52
+ | Variable | Value | Description |
53
+ | ------------------------ | ----------------- | ---------------------------------------------- |
54
+ | `TWIN_HOST` | `0.0.0.0` | Listen on all interfaces inside the container. |
55
+ | `TWIN_PORT` | `3000` | The port the API server listens on. |
56
+ | `TWIN_STORAGE_FILE_ROOT` | `/twin-node/data` | The root directory for `file` storage. |
57
+
58
+ Change any of them with `-e`, for example `-e TWIN_PORT=8080`.
59
+
60
+ ## Data Persistence
61
+
62
+ When storage is configured to use the `file` connector, its data and the engine state in `engine-state.json` are written to `/twin-node/data`. Mount a volume or a host directory there so that the data is kept when the container is replaced:
63
+
64
+ ```shell
65
+ -v /home/twin-node/data:/twin-node/data
66
+ ```
67
+
68
+ The same data directory must be mounted for the bootstrap commands and for the running node.
69
+
70
+ ## Bootstrapping
71
+
72
+ The node will not start until it has been bootstrapped. A container started without a node identity stops with:
73
+
74
+ ```shell
75
+ The node identity is enabled in config but is not set, please set it using the "node-identity-set" CLI command.
76
+ ```
77
+
78
+ The CLI commands run from the same image by passing the command after `node src/index.js`, with the same configuration and data directory that the node will use. For a development node the [bootstrap-dev](./commands/bootstrap-dev.md) command creates the node identity, organisation identity and admin user in a single step:
79
+
80
+ ```shell
81
+ docker run --rm \
82
+ -v /home/twin-node/.env:/app/.env:ro \
83
+ -v /home/twin-node/.env.bootstrap-dev:/app/.env.bootstrap-dev:ro \
84
+ -v /home/twin-node/data:/twin-node/data \
85
+ 3sixty-node node src/index.js bootstrap-dev --load-env=.env.bootstrap-dev
86
+ ```
87
+
88
+ The values it reads are described in [bootstrap-dev](./commands/bootstrap-dev.md), and `.env.example-bootstrap-dev` is a starting point for the file. Any value that is not provided is generated. Generated mnemonics and passwords are only shown in the command output, so record them.
89
+
90
+ To bootstrap a production node step by step, or to manage identities, tenants and users later, run the relevant commands from [Node Usage](./usage.md) in the same way. Use `help` to list the commands available in the image:
91
+
92
+ ```shell
93
+ docker run --rm 3sixty-node node src/index.js help
94
+ ```
95
+
96
+ ## Running the Node
97
+
98
+ Once bootstrapped, start the node with the same configuration and data directory:
99
+
100
+ ```shell
101
+ docker run -d --name 3sixty-node \
102
+ -v /home/twin-node/.env:/app/.env:ro \
103
+ -v /home/twin-node/data:/twin-node/data \
104
+ -p 3000:3000 \
105
+ 3sixty-node
106
+ ```
107
+
108
+ Follow the output with `docker logs -f 3sixty-node`. When the node is ready you should see:
109
+
110
+ ```shell
111
+ INFO [2026-09-24T04:57:23.320Z] EngineCore Engine has started
112
+ INFO [2026-09-24T04:57:23.346Z] FastifyWebServer Building Web Server
113
+ INFO [2026-09-24T04:57:23.384Z] FastifyWebServer Starting Web Server with binding "0.0.0.0" on port "3000"
114
+ INFO [2026-09-24T04:57:23.392Z] FastifyWebServer The Web Server started on http://127.0.0.1:3000
115
+ ```
116
+
117
+ You should now be able to access the server in the browser at [http://localhost:3000/info](http://localhost:3000/info), which returns something similar to:
118
+
119
+ ```json
120
+ {
121
+ "name": "3Sixty Node",
122
+ "version": "0.10.1-next.6"
123
+ }
124
+ ```
125
+
126
+ The request and response are also shown in the logs:
127
+
128
+ ```shell
129
+ INFO [2026-09-24T04:57:25.150Z] LoggingProcessor ===> GET /info {}
130
+ INFO [2026-09-24T04:57:25.150Z] LoggingProcessor <=== 200 GET /info duration: 722µs {"body":{"name":"3Sixty Node","version":"0.10.1-next.6"}}
131
+ ```
132
+
133
+ ## Stopping the Node
134
+
135
+ The node responds to the standard termination signals, so `docker stop 3sixty-node` stops the components gracefully before the container exits:
136
+
137
+ ```shell
138
+ INFO [2026-09-24T05:00:09.380Z] EngineCore Components have stopped
139
+ INFO [2026-09-24T05:00:09.380Z] EngineCore Engine has stopped
140
+ ```
141
+
142
+ Starting the container again with `docker start 3sixty-node` reuses the existing engine state, so the bootstrap does not need to be repeated.