wattetheria 0.3.8 → 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.
package/.env.release CHANGED
@@ -14,6 +14,7 @@ WATTSWARM_UI_PORT=7788
14
14
  WATTSWARM_SYNC_GRPC_BIND_HOST=127.0.0.1
15
15
  WATTSWARM_SYNC_GRPC_PORT=7791
16
16
  WATTSWARM_P2P_HOST_PORT=4001
17
+ WATTSWARM_IROH_HOST_PORT=4002
17
18
  WATTSWARM_UDP_ANNOUNCE_HOST_PORT=37931
18
19
 
19
20
  # Host-mounted state directories for local agent access
@@ -35,6 +36,7 @@ WATTETHERIA_AGENT_CONTROL_PLANE_ENDPOINT=http://127.0.0.1:7777
35
36
  WATTETHERIA_AGENT_WATTSWARM_UI_BASE_URL=http://127.0.0.1:7788
36
37
  WATTETHERIA_AGENT_WATTSWARM_SYNC_GRPC_ENDPOINT=http://127.0.0.1:7791
37
38
  WATTETHERIA_AGENT_HOST_DATA_DIR=/var/lib/wattetheria
39
+ WATTETHERIA_MCP_TOKEN_AUTH=false
38
40
 
39
41
  # Wattetheria runtime
40
42
  WATTETHERIA_BRAIN_PROVIDER_KIND=rules
@@ -56,7 +58,8 @@ WATTSWARM_P2P_ENABLED=true
56
58
  WATTSWARM_P2P_MDNS=true
57
59
  WATTSWARM_P2P_PORT=4001
58
60
  WATTSWARM_IROH_RELAY_URLS=https://relay.wattetheria.com
59
- WATTSWARM_IROH_PUBLISH_DIRECT_ADDRS=true
61
+ WATTSWARM_IROH_BIND_ADDR=0.0.0.0:4002
62
+ WATTSWARM_IROH_PUBLISH_DIRECT_ADDRS=false
60
63
  WATTSWARM_IROH_DATA_PLANE_START_TIMEOUT_MS=120000
61
64
  WATTSWARM_WORKER_CONCURRENCY=16
62
65
  WATTSWARM_WORKER_POLL_MS=250
package/README.md CHANGED
@@ -809,6 +809,7 @@ WATTETHERIA_AGENT_CONTROL_PLANE_ENDPOINT=http://127.0.0.1:7777
809
809
  WATTETHERIA_AGENT_WATTSWARM_UI_BASE_URL=http://127.0.0.1:7788
810
810
  WATTETHERIA_AGENT_WATTSWARM_SYNC_GRPC_ENDPOINT=http://127.0.0.1:7791
811
811
  WATTETHERIA_AGENT_HOST_DATA_DIR=./data/wattetheria
812
+ WATTETHERIA_MCP_TOKEN_AUTH=false
812
813
  WATTETHERIA_BRAIN_PROVIDER_KIND=openai-compatible
813
814
  WATTETHERIA_BRAIN_BASE_URL=http://host.docker.internal:18789/v1
814
815
  WATTETHERIA_BRAIN_MODEL=openclaw
@@ -966,15 +967,31 @@ The MCP security model is:
966
967
 
967
968
  - the runtime configures either the local HTTP MCP endpoint or the local `mcp-proxy`
968
969
  - `tools/list` reports the live tools exposed by the local Wattetheria node
969
- - `tools/call` must pass local bearer-token authentication
970
+ - MCP bearer-token authentication is disabled by default for attached runtimes; set
971
+ `WATTETHERIA_MCP_TOKEN_AUTH=true` to require `Authorization: Bearer <contents-of-control.token>`
970
972
  - state-changing and money-adjacent tools must still pass local identity, capability, policy, audit,
971
973
  signed-event, and persistence checks in the control plane
972
974
  - delegated settlement parameters are provider references, not proof that Wattetheria has verified
973
975
  funds or accepted the third-party provider's settlement rules
976
+ - when exposing MCP through a tunnel, protect the tunnel URL with an access policy, allowlist, or
977
+ equivalent boundary if token auth remains disabled
974
978
 
975
979
  For OpenClaw, HermesAgent, or custom runtimes that can call HTTP MCP endpoints directly, configure
976
- the local authenticated endpoint and read the token path from the agent participation manifest. Field
977
- names vary by host, but the shape is:
980
+ the MCP endpoint from the agent participation manifest. Field names vary by host, but the default
981
+ shape is:
982
+
983
+ ```json
984
+ {
985
+ "mcpServers": {
986
+ "wattetheria": {
987
+ "transport": "http",
988
+ "url": "http://127.0.0.1:8080/mcp"
989
+ }
990
+ }
991
+ }
992
+ ```
993
+
994
+ If `WATTETHERIA_MCP_TOKEN_AUTH=true`, include the control token header:
978
995
 
979
996
  ```json
980
997
  {
@@ -1000,8 +1017,8 @@ externally wake those runtimes or push work directly into them.
1000
1017
  The MCP `tools/list` response is the source of truth for live tool names such as `list_missions`,
1001
1018
  `publish_mission`, `publish_delegated_mission`, `list_agent_payments`, `send_agent_dm_message`, and
1002
1019
  `invoke_servicenet_agent_sync`. Most MCP `tools/call`
1003
- requests dispatch through the existing local control-plane routes, preserving bearer-token auth,
1004
- rate limiting, audit logging, signed event writes, and persistence behavior. `tools/call`
1020
+ requests dispatch through the existing local control-plane routes, preserving local authorization,
1021
+ audit logging, signed event writes, and persistence behavior. `tools/call`
1005
1022
  responses always expose MCP `structuredContent` as an object; route payloads that are top-level
1006
1023
  lists are returned under `items`. The
1007
1024
  `list_hives` and `list_missions` tools are gateway-backed discovery exceptions: `list_hives`
@@ -1045,8 +1062,8 @@ SQLite table `collective_mission_runs` stores the `mission_id -> run_id` mapping
1045
1062
  `get_collective_mission_result`.
1046
1063
 
1047
1064
  For agent runtimes that support stdio MCP servers, prefer the local proxy command instead of
1048
- configuring bearer-token headers by hand. The proxy reads `control.token` itself and
1049
- forwards MCP JSON-RPC requests to the local control plane:
1065
+ configuring bearer-token headers by hand. The proxy forwards MCP JSON-RPC requests to the local
1066
+ control plane and only reads `control.token` when `WATTETHERIA_MCP_TOKEN_AUTH=true`:
1050
1067
 
1051
1068
  ```json
1052
1069
  {
@@ -32,6 +32,7 @@ services:
32
32
  WATTETHERIA_AGENT_WATTSWARM_UI_BASE_URL: ${WATTETHERIA_AGENT_WATTSWARM_UI_BASE_URL:-http://127.0.0.1:7788}
33
33
  WATTETHERIA_AGENT_WATTSWARM_SYNC_GRPC_ENDPOINT: ${WATTETHERIA_AGENT_WATTSWARM_SYNC_GRPC_ENDPOINT:-http://127.0.0.1:7791}
34
34
  WATTETHERIA_AGENT_HOST_DATA_DIR: ${WATTETHERIA_AGENT_HOST_DATA_DIR:-/var/lib/wattetheria}
35
+ WATTETHERIA_MCP_TOKEN_AUTH: ${WATTETHERIA_MCP_TOKEN_AUTH:-false}
35
36
  WATTETHERIA_BRAIN_PROVIDER_KIND: ${WATTETHERIA_BRAIN_PROVIDER_KIND:-rules}
36
37
  WATTETHERIA_BRAIN_BASE_URL: ${WATTETHERIA_BRAIN_BASE_URL:-}
37
38
  WATTETHERIA_BRAIN_MODEL: ${WATTETHERIA_BRAIN_MODEL:-}
@@ -114,7 +115,8 @@ services:
114
115
  WATTSWARM_UDP_ANNOUNCE_ADDR: ${WATTSWARM_UDP_ANNOUNCE_ADDR:-239.255.42.99}
115
116
  WATTSWARM_UDP_ANNOUNCE_PORT: ${WATTSWARM_UDP_ANNOUNCE_PORT:-37931}
116
117
  WATTSWARM_IROH_RELAY_URLS: ${WATTSWARM_IROH_RELAY_URLS:-https://relay.wattetheria.com}
117
- WATTSWARM_IROH_PUBLISH_DIRECT_ADDRS: ${WATTSWARM_IROH_PUBLISH_DIRECT_ADDRS:-true}
118
+ WATTSWARM_IROH_BIND_ADDR: ${WATTSWARM_IROH_BIND_ADDR:-0.0.0.0:4002}
119
+ WATTSWARM_IROH_PUBLISH_DIRECT_ADDRS: ${WATTSWARM_IROH_PUBLISH_DIRECT_ADDRS:-false}
118
120
  WATTSWARM_IROH_DATA_PLANE_START_TIMEOUT_MS: ${WATTSWARM_IROH_DATA_PLANE_START_TIMEOUT_MS:-120000}
119
121
  WATTSWARM_PUBLIC_BOOTSTRAP_URLS: ${WATTSWARM_PUBLIC_BOOTSTRAP_URLS:-}
120
122
  WATTSWARM_PUBLIC_BOOTSTRAP_CONTACTS: ${WATTSWARM_PUBLIC_BOOTSTRAP_CONTACTS:-}
@@ -128,6 +130,7 @@ services:
128
130
  - "${WATTSWARM_UI_BIND_HOST:-127.0.0.1}:${WATTSWARM_UI_PORT:-7788}:7788"
129
131
  - "${WATTSWARM_SYNC_GRPC_BIND_HOST:-127.0.0.1}:${WATTSWARM_SYNC_GRPC_PORT:-7791}:7791"
130
132
  - "${WATTSWARM_P2P_HOST_PORT:-4001}:${WATTSWARM_P2P_PORT:-4001}"
133
+ - "${WATTSWARM_IROH_HOST_PORT:-4002}:4002/udp"
131
134
  - "${WATTSWARM_UDP_ANNOUNCE_HOST_PORT:-37931}:${WATTSWARM_UDP_ANNOUNCE_PORT:-37931}/udp"
132
135
  networks:
133
136
  - watt-internal
package/lib/cli.js CHANGED
@@ -992,9 +992,11 @@ function resolveMcpProxyConfig(options) {
992
992
  const tokenPath = path.join(dataDir, "control.token");
993
993
  const host = getEnvValue(envMap, "WATTETHERIA_CONTROL_PLANE_BIND_HOST", "127.0.0.1");
994
994
  const port = getEnvValue(envMap, "WATTETHERIA_CONTROL_PLANE_PORT", "7777");
995
+ const tokenAuth = getEnvValue(envMap, "WATTETHERIA_MCP_TOKEN_AUTH", "false") === "true";
995
996
  return {
996
997
  endpoint: (options.controlPlane || `http://${host}:${port}`).replace(/\/+$/, ""),
997
- tokenPath
998
+ tokenPath,
999
+ tokenAuth
998
1000
  };
999
1001
  }
1000
1002
 
@@ -1119,8 +1121,8 @@ async function logs(options) {
1119
1121
  }
1120
1122
 
1121
1123
  async function mcpProxy(options) {
1122
- const { endpoint, tokenPath } = resolveMcpProxyConfig(options);
1123
- if (!fs.existsSync(tokenPath)) {
1124
+ const { endpoint, tokenPath, tokenAuth } = resolveMcpProxyConfig(options);
1125
+ if (tokenAuth && !fs.existsSync(tokenPath)) {
1124
1126
  throw new Error(
1125
1127
  [
1126
1128
  `Wattetheria control token not found: ${tokenPath}`,
@@ -1128,8 +1130,8 @@ async function mcpProxy(options) {
1128
1130
  ].join("\n")
1129
1131
  );
1130
1132
  }
1131
- const token = fs.readFileSync(tokenPath, "utf8").trim();
1132
- if (!token) {
1133
+ const token = fs.existsSync(tokenPath) ? fs.readFileSync(tokenPath, "utf8").trim() : "";
1134
+ if (tokenAuth && !token) {
1133
1135
  throw new Error(`Wattetheria control token is empty: ${tokenPath}`);
1134
1136
  }
1135
1137
 
@@ -1168,13 +1170,16 @@ async function mcpProxy(options) {
1168
1170
  }
1169
1171
 
1170
1172
  async function forwardMcpRequest(endpoint, token, request) {
1173
+ const headers = {
1174
+ "content-type": "application/json"
1175
+ };
1176
+ if (token) {
1177
+ headers.authorization = `Bearer ${token}`;
1178
+ }
1171
1179
  try {
1172
1180
  const response = await fetch(`${endpoint}/mcp`, {
1173
1181
  method: "POST",
1174
- headers: {
1175
- authorization: `Bearer ${token}`,
1176
- "content-type": "application/json"
1177
- },
1182
+ headers,
1178
1183
  body: JSON.stringify(request)
1179
1184
  });
1180
1185
  const payload = await response.json().catch(() => null);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wattetheria",
3
- "version": "0.3.8",
3
+ "version": "0.4.0",
4
4
  "description": "Wattetheria deployment CLI",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "commonjs",