clawdentity 0.0.21 → 0.0.22

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/dist/bin.js CHANGED
@@ -15719,20 +15719,56 @@ var registryConfigSchema = external_exports.object({
15719
15719
  REGISTRY_SIGNING_KEY: external_exports.string().min(1).optional(),
15720
15720
  REGISTRY_SIGNING_KEYS: registrySigningKeysEnvSchema.optional()
15721
15721
  });
15722
- function parseRegistryConfig(env) {
15723
- const parsed = registryConfigSchema.safeParse(env);
15724
- if (parsed.success) {
15725
- return parsed.data;
15726
- }
15722
+ var REQUIRED_REGISTRY_RUNTIME_KEYS = [
15723
+ "PROXY_URL",
15724
+ "REGISTRY_ISSUER_URL",
15725
+ "EVENT_BUS_BACKEND",
15726
+ "BOOTSTRAP_SECRET",
15727
+ "REGISTRY_SIGNING_KEY",
15728
+ "REGISTRY_SIGNING_KEYS"
15729
+ ];
15730
+ function throwRegistryConfigValidationError(details) {
15727
15731
  throw new AppError({
15728
15732
  code: "CONFIG_VALIDATION_FAILED",
15729
15733
  message: "Registry configuration is invalid",
15730
15734
  status: 500,
15731
15735
  expose: true,
15732
- details: {
15733
- fieldErrors: parsed.error.flatten().fieldErrors,
15734
- formErrors: parsed.error.flatten().formErrors
15736
+ details
15737
+ });
15738
+ }
15739
+ function assertRequiredRegistryRuntimeKeys(input) {
15740
+ if (input.ENVIRONMENT === "test") {
15741
+ return;
15742
+ }
15743
+ const fieldErrors = {};
15744
+ for (const key of REQUIRED_REGISTRY_RUNTIME_KEYS) {
15745
+ const value = input[key];
15746
+ if (typeof value === "string" && value.trim().length > 0) {
15747
+ continue;
15748
+ }
15749
+ if (value !== void 0 && value !== null && !(typeof value === "string" && value.trim().length === 0)) {
15750
+ continue;
15751
+ }
15752
+ fieldErrors[key] = [`${key} is required`];
15753
+ }
15754
+ if (Object.keys(fieldErrors).length > 0) {
15755
+ throwRegistryConfigValidationError({
15756
+ fieldErrors,
15757
+ formErrors: []
15758
+ });
15759
+ }
15760
+ }
15761
+ function parseRegistryConfig(env, options = {}) {
15762
+ const parsed = registryConfigSchema.safeParse(env);
15763
+ if (parsed.success) {
15764
+ if (options.requireRuntimeKeys === true) {
15765
+ assertRequiredRegistryRuntimeKeys(parsed.data);
15735
15766
  }
15767
+ return parsed.data;
15768
+ }
15769
+ throwRegistryConfigValidationError({
15770
+ fieldErrors: parsed.error.flatten().fieldErrors,
15771
+ formErrors: parsed.error.flatten().formErrors
15736
15772
  });
15737
15773
  }
15738
15774
 
@@ -21760,6 +21796,7 @@ var OPENCLAW_SETUP_RESTART_COMMAND_HINT = `${OPENCLAW_SETUP_COMMAND_HINT} and re
21760
21796
  var OPENCLAW_SETUP_WITH_BASE_URL_HINT = `${OPENCLAW_SETUP_COMMAND_HINT} --openclaw-base-url <url>`;
21761
21797
  var OPENCLAW_PAIRING_COMMAND_HINT = "Run QR pairing first: clawdentity pair start <agentName> --qr and clawdentity pair confirm <agentName> --qr-file <path>";
21762
21798
  var OPENCLAW_DEVICE_APPROVAL_RECOVERY_HINT = "Run: clawdentity openclaw setup <agentName> (auto-recovers pending OpenClaw gateway device approvals)";
21799
+ var OPENCLAW_GATEWAY_AUTH_RECOVERY_HINT = "Run: clawdentity openclaw setup <agentName> (ensures gateway auth mode/token are configured)";
21763
21800
  var OPENCLAW_GATEWAY_APPROVAL_COMMAND = "openclaw";
21764
21801
  var OPENCLAW_GATEWAY_APPROVAL_TIMEOUT_MS = 1e4;
21765
21802
  var textEncoder2 = new TextEncoder();
@@ -22691,6 +22728,28 @@ function isCanonicalAgentSessionKey(value) {
22691
22728
  function generateOpenclawHookToken() {
22692
22729
  return randomBytes3(OPENCLAW_HOOK_TOKEN_BYTES).toString("hex");
22693
22730
  }
22731
+ function generateOpenclawGatewayToken() {
22732
+ return randomBytes3(OPENCLAW_HOOK_TOKEN_BYTES).toString("hex");
22733
+ }
22734
+ function parseGatewayAuthMode(value) {
22735
+ if (typeof value !== "string") {
22736
+ return void 0;
22737
+ }
22738
+ const normalized = value.trim().toLowerCase();
22739
+ if (normalized === "token" || normalized === "password" || normalized === "trusted-proxy") {
22740
+ return normalized;
22741
+ }
22742
+ return void 0;
22743
+ }
22744
+ function resolveEnvOpenclawGatewayToken() {
22745
+ if (typeof process.env.OPENCLAW_GATEWAY_TOKEN === "string" && process.env.OPENCLAW_GATEWAY_TOKEN.trim().length > 0) {
22746
+ return process.env.OPENCLAW_GATEWAY_TOKEN.trim();
22747
+ }
22748
+ return void 0;
22749
+ }
22750
+ function resolveGatewayAuthToken(existingToken) {
22751
+ return resolveEnvOpenclawGatewayToken() ?? existingToken ?? generateOpenclawGatewayToken();
22752
+ }
22694
22753
  function upsertRelayHookMapping(mappingsValue) {
22695
22754
  const mappings = Array.isArray(mappingsValue) ? mappingsValue.filter(isRecord9).map((mapping) => ({ ...mapping })) : [];
22696
22755
  const existingIndex = mappings.findIndex((mapping) => {
@@ -22757,9 +22816,22 @@ async function patchOpenclawConfig(openclawConfigPath, hookToken) {
22757
22816
  ["hook:", defaultSessionKey]
22758
22817
  );
22759
22818
  hooks.mappings = upsertRelayHookMapping(hooks.mappings);
22819
+ const gateway = isRecord9(config2.gateway) ? { ...config2.gateway } : {};
22820
+ const gatewayAuth = isRecord9(gateway.auth) ? { ...gateway.auth } : {};
22821
+ const configuredGatewayAuthMode = parseGatewayAuthMode(gatewayAuth.mode);
22822
+ if (configuredGatewayAuthMode === void 0) {
22823
+ gatewayAuth.mode = "token";
22824
+ }
22825
+ const effectiveGatewayAuthMode = parseGatewayAuthMode(gatewayAuth.mode) ?? "token";
22826
+ if (effectiveGatewayAuthMode === "token") {
22827
+ const existingGatewayAuthToken = typeof gatewayAuth.token === "string" && gatewayAuth.token.trim().length > 0 ? gatewayAuth.token.trim() : void 0;
22828
+ gatewayAuth.token = resolveGatewayAuthToken(existingGatewayAuthToken);
22829
+ }
22830
+ gateway.auth = gatewayAuth;
22760
22831
  const nextConfig = {
22761
22832
  ...config2,
22762
- hooks
22833
+ hooks,
22834
+ gateway
22763
22835
  };
22764
22836
  await writeFile6(
22765
22837
  openclawConfigPath,
@@ -23264,6 +23336,79 @@ async function runOpenclawDoctor(options = {}) {
23264
23336
  })
23265
23337
  );
23266
23338
  }
23339
+ const gateway = isRecord9(openclawConfig.gateway) ? openclawConfig.gateway : {};
23340
+ const gatewayAuth = isRecord9(gateway.auth) ? gateway.auth : {};
23341
+ const gatewayAuthMode = parseGatewayAuthMode(gatewayAuth.mode);
23342
+ const gatewayAuthToken = typeof gatewayAuth.token === "string" && gatewayAuth.token.trim().length > 0 ? gatewayAuth.token.trim() : void 0;
23343
+ const gatewayAuthPassword = typeof gatewayAuth.password === "string" && gatewayAuth.password.trim().length > 0 ? gatewayAuth.password.trim() : void 0;
23344
+ if (gatewayAuthMode === "token") {
23345
+ if (gatewayAuthToken === void 0) {
23346
+ checks.push(
23347
+ toDoctorCheck({
23348
+ id: "state.gatewayAuth",
23349
+ label: "OpenClaw gateway auth",
23350
+ status: "fail",
23351
+ message: `gateway.auth.token is missing in ${openclawConfigPath}`,
23352
+ remediationHint: OPENCLAW_GATEWAY_AUTH_RECOVERY_HINT,
23353
+ details: { openclawConfigPath, gatewayAuthMode }
23354
+ })
23355
+ );
23356
+ } else {
23357
+ checks.push(
23358
+ toDoctorCheck({
23359
+ id: "state.gatewayAuth",
23360
+ label: "OpenClaw gateway auth",
23361
+ status: "pass",
23362
+ message: "gateway auth is configured with token mode",
23363
+ details: { openclawConfigPath, gatewayAuthMode }
23364
+ })
23365
+ );
23366
+ }
23367
+ } else if (gatewayAuthMode === "password") {
23368
+ if (gatewayAuthPassword === void 0) {
23369
+ checks.push(
23370
+ toDoctorCheck({
23371
+ id: "state.gatewayAuth",
23372
+ label: "OpenClaw gateway auth",
23373
+ status: "fail",
23374
+ message: `gateway.auth.password is missing in ${openclawConfigPath}`,
23375
+ remediationHint: OPENCLAW_GATEWAY_AUTH_RECOVERY_HINT,
23376
+ details: { openclawConfigPath, gatewayAuthMode }
23377
+ })
23378
+ );
23379
+ } else {
23380
+ checks.push(
23381
+ toDoctorCheck({
23382
+ id: "state.gatewayAuth",
23383
+ label: "OpenClaw gateway auth",
23384
+ status: "pass",
23385
+ message: "gateway auth is configured with password mode",
23386
+ details: { openclawConfigPath, gatewayAuthMode }
23387
+ })
23388
+ );
23389
+ }
23390
+ } else if (gatewayAuthMode === "trusted-proxy") {
23391
+ checks.push(
23392
+ toDoctorCheck({
23393
+ id: "state.gatewayAuth",
23394
+ label: "OpenClaw gateway auth",
23395
+ status: "pass",
23396
+ message: "gateway auth is configured with trusted-proxy mode",
23397
+ details: { openclawConfigPath, gatewayAuthMode }
23398
+ })
23399
+ );
23400
+ } else {
23401
+ checks.push(
23402
+ toDoctorCheck({
23403
+ id: "state.gatewayAuth",
23404
+ label: "OpenClaw gateway auth",
23405
+ status: "fail",
23406
+ message: `gateway.auth.mode is missing or unsupported in ${openclawConfigPath}`,
23407
+ remediationHint: OPENCLAW_GATEWAY_AUTH_RECOVERY_HINT,
23408
+ details: { openclawConfigPath }
23409
+ })
23410
+ );
23411
+ }
23267
23412
  } catch {
23268
23413
  checks.push(
23269
23414
  toDoctorCheck({
@@ -23295,6 +23440,16 @@ async function runOpenclawDoctor(options = {}) {
23295
23440
  details: { openclawConfigPath }
23296
23441
  })
23297
23442
  );
23443
+ checks.push(
23444
+ toDoctorCheck({
23445
+ id: "state.gatewayAuth",
23446
+ label: "OpenClaw gateway auth",
23447
+ status: "fail",
23448
+ message: `unable to read ${openclawConfigPath}`,
23449
+ remediationHint: "Ensure the OpenClaw config file exists (OPENCLAW_CONFIG_PATH/CLAWDBOT_CONFIG_PATH, or state dir) and rerun openclaw setup",
23450
+ details: { openclawConfigPath }
23451
+ })
23452
+ );
23298
23453
  }
23299
23454
  const relayRuntimeConfigPath = resolveRelayRuntimeConfigPath(homeDir);
23300
23455
  try {
package/dist/index.js CHANGED
@@ -15723,20 +15723,56 @@ var registryConfigSchema = external_exports.object({
15723
15723
  REGISTRY_SIGNING_KEY: external_exports.string().min(1).optional(),
15724
15724
  REGISTRY_SIGNING_KEYS: registrySigningKeysEnvSchema.optional()
15725
15725
  });
15726
- function parseRegistryConfig(env) {
15727
- const parsed = registryConfigSchema.safeParse(env);
15728
- if (parsed.success) {
15729
- return parsed.data;
15730
- }
15726
+ var REQUIRED_REGISTRY_RUNTIME_KEYS = [
15727
+ "PROXY_URL",
15728
+ "REGISTRY_ISSUER_URL",
15729
+ "EVENT_BUS_BACKEND",
15730
+ "BOOTSTRAP_SECRET",
15731
+ "REGISTRY_SIGNING_KEY",
15732
+ "REGISTRY_SIGNING_KEYS"
15733
+ ];
15734
+ function throwRegistryConfigValidationError(details) {
15731
15735
  throw new AppError({
15732
15736
  code: "CONFIG_VALIDATION_FAILED",
15733
15737
  message: "Registry configuration is invalid",
15734
15738
  status: 500,
15735
15739
  expose: true,
15736
- details: {
15737
- fieldErrors: parsed.error.flatten().fieldErrors,
15738
- formErrors: parsed.error.flatten().formErrors
15740
+ details
15741
+ });
15742
+ }
15743
+ function assertRequiredRegistryRuntimeKeys(input) {
15744
+ if (input.ENVIRONMENT === "test") {
15745
+ return;
15746
+ }
15747
+ const fieldErrors = {};
15748
+ for (const key of REQUIRED_REGISTRY_RUNTIME_KEYS) {
15749
+ const value = input[key];
15750
+ if (typeof value === "string" && value.trim().length > 0) {
15751
+ continue;
15752
+ }
15753
+ if (value !== void 0 && value !== null && !(typeof value === "string" && value.trim().length === 0)) {
15754
+ continue;
15755
+ }
15756
+ fieldErrors[key] = [`${key} is required`];
15757
+ }
15758
+ if (Object.keys(fieldErrors).length > 0) {
15759
+ throwRegistryConfigValidationError({
15760
+ fieldErrors,
15761
+ formErrors: []
15762
+ });
15763
+ }
15764
+ }
15765
+ function parseRegistryConfig(env, options = {}) {
15766
+ const parsed = registryConfigSchema.safeParse(env);
15767
+ if (parsed.success) {
15768
+ if (options.requireRuntimeKeys === true) {
15769
+ assertRequiredRegistryRuntimeKeys(parsed.data);
15739
15770
  }
15771
+ return parsed.data;
15772
+ }
15773
+ throwRegistryConfigValidationError({
15774
+ fieldErrors: parsed.error.flatten().fieldErrors,
15775
+ formErrors: parsed.error.flatten().formErrors
15740
15776
  });
15741
15777
  }
15742
15778
 
@@ -21760,6 +21796,7 @@ var OPENCLAW_SETUP_RESTART_COMMAND_HINT = `${OPENCLAW_SETUP_COMMAND_HINT} and re
21760
21796
  var OPENCLAW_SETUP_WITH_BASE_URL_HINT = `${OPENCLAW_SETUP_COMMAND_HINT} --openclaw-base-url <url>`;
21761
21797
  var OPENCLAW_PAIRING_COMMAND_HINT = "Run QR pairing first: clawdentity pair start <agentName> --qr and clawdentity pair confirm <agentName> --qr-file <path>";
21762
21798
  var OPENCLAW_DEVICE_APPROVAL_RECOVERY_HINT = "Run: clawdentity openclaw setup <agentName> (auto-recovers pending OpenClaw gateway device approvals)";
21799
+ var OPENCLAW_GATEWAY_AUTH_RECOVERY_HINT = "Run: clawdentity openclaw setup <agentName> (ensures gateway auth mode/token are configured)";
21763
21800
  var OPENCLAW_GATEWAY_APPROVAL_COMMAND = "openclaw";
21764
21801
  var OPENCLAW_GATEWAY_APPROVAL_TIMEOUT_MS = 1e4;
21765
21802
  var textEncoder2 = new TextEncoder();
@@ -22691,6 +22728,28 @@ function isCanonicalAgentSessionKey(value) {
22691
22728
  function generateOpenclawHookToken() {
22692
22729
  return randomBytes3(OPENCLAW_HOOK_TOKEN_BYTES).toString("hex");
22693
22730
  }
22731
+ function generateOpenclawGatewayToken() {
22732
+ return randomBytes3(OPENCLAW_HOOK_TOKEN_BYTES).toString("hex");
22733
+ }
22734
+ function parseGatewayAuthMode(value) {
22735
+ if (typeof value !== "string") {
22736
+ return void 0;
22737
+ }
22738
+ const normalized = value.trim().toLowerCase();
22739
+ if (normalized === "token" || normalized === "password" || normalized === "trusted-proxy") {
22740
+ return normalized;
22741
+ }
22742
+ return void 0;
22743
+ }
22744
+ function resolveEnvOpenclawGatewayToken() {
22745
+ if (typeof process.env.OPENCLAW_GATEWAY_TOKEN === "string" && process.env.OPENCLAW_GATEWAY_TOKEN.trim().length > 0) {
22746
+ return process.env.OPENCLAW_GATEWAY_TOKEN.trim();
22747
+ }
22748
+ return void 0;
22749
+ }
22750
+ function resolveGatewayAuthToken(existingToken) {
22751
+ return resolveEnvOpenclawGatewayToken() ?? existingToken ?? generateOpenclawGatewayToken();
22752
+ }
22694
22753
  function upsertRelayHookMapping(mappingsValue) {
22695
22754
  const mappings = Array.isArray(mappingsValue) ? mappingsValue.filter(isRecord9).map((mapping) => ({ ...mapping })) : [];
22696
22755
  const existingIndex = mappings.findIndex((mapping) => {
@@ -22757,9 +22816,22 @@ async function patchOpenclawConfig(openclawConfigPath, hookToken) {
22757
22816
  ["hook:", defaultSessionKey]
22758
22817
  );
22759
22818
  hooks.mappings = upsertRelayHookMapping(hooks.mappings);
22819
+ const gateway = isRecord9(config2.gateway) ? { ...config2.gateway } : {};
22820
+ const gatewayAuth = isRecord9(gateway.auth) ? { ...gateway.auth } : {};
22821
+ const configuredGatewayAuthMode = parseGatewayAuthMode(gatewayAuth.mode);
22822
+ if (configuredGatewayAuthMode === void 0) {
22823
+ gatewayAuth.mode = "token";
22824
+ }
22825
+ const effectiveGatewayAuthMode = parseGatewayAuthMode(gatewayAuth.mode) ?? "token";
22826
+ if (effectiveGatewayAuthMode === "token") {
22827
+ const existingGatewayAuthToken = typeof gatewayAuth.token === "string" && gatewayAuth.token.trim().length > 0 ? gatewayAuth.token.trim() : void 0;
22828
+ gatewayAuth.token = resolveGatewayAuthToken(existingGatewayAuthToken);
22829
+ }
22830
+ gateway.auth = gatewayAuth;
22760
22831
  const nextConfig = {
22761
22832
  ...config2,
22762
- hooks
22833
+ hooks,
22834
+ gateway
22763
22835
  };
22764
22836
  await writeFile6(
22765
22837
  openclawConfigPath,
@@ -23264,6 +23336,79 @@ async function runOpenclawDoctor(options = {}) {
23264
23336
  })
23265
23337
  );
23266
23338
  }
23339
+ const gateway = isRecord9(openclawConfig.gateway) ? openclawConfig.gateway : {};
23340
+ const gatewayAuth = isRecord9(gateway.auth) ? gateway.auth : {};
23341
+ const gatewayAuthMode = parseGatewayAuthMode(gatewayAuth.mode);
23342
+ const gatewayAuthToken = typeof gatewayAuth.token === "string" && gatewayAuth.token.trim().length > 0 ? gatewayAuth.token.trim() : void 0;
23343
+ const gatewayAuthPassword = typeof gatewayAuth.password === "string" && gatewayAuth.password.trim().length > 0 ? gatewayAuth.password.trim() : void 0;
23344
+ if (gatewayAuthMode === "token") {
23345
+ if (gatewayAuthToken === void 0) {
23346
+ checks.push(
23347
+ toDoctorCheck({
23348
+ id: "state.gatewayAuth",
23349
+ label: "OpenClaw gateway auth",
23350
+ status: "fail",
23351
+ message: `gateway.auth.token is missing in ${openclawConfigPath}`,
23352
+ remediationHint: OPENCLAW_GATEWAY_AUTH_RECOVERY_HINT,
23353
+ details: { openclawConfigPath, gatewayAuthMode }
23354
+ })
23355
+ );
23356
+ } else {
23357
+ checks.push(
23358
+ toDoctorCheck({
23359
+ id: "state.gatewayAuth",
23360
+ label: "OpenClaw gateway auth",
23361
+ status: "pass",
23362
+ message: "gateway auth is configured with token mode",
23363
+ details: { openclawConfigPath, gatewayAuthMode }
23364
+ })
23365
+ );
23366
+ }
23367
+ } else if (gatewayAuthMode === "password") {
23368
+ if (gatewayAuthPassword === void 0) {
23369
+ checks.push(
23370
+ toDoctorCheck({
23371
+ id: "state.gatewayAuth",
23372
+ label: "OpenClaw gateway auth",
23373
+ status: "fail",
23374
+ message: `gateway.auth.password is missing in ${openclawConfigPath}`,
23375
+ remediationHint: OPENCLAW_GATEWAY_AUTH_RECOVERY_HINT,
23376
+ details: { openclawConfigPath, gatewayAuthMode }
23377
+ })
23378
+ );
23379
+ } else {
23380
+ checks.push(
23381
+ toDoctorCheck({
23382
+ id: "state.gatewayAuth",
23383
+ label: "OpenClaw gateway auth",
23384
+ status: "pass",
23385
+ message: "gateway auth is configured with password mode",
23386
+ details: { openclawConfigPath, gatewayAuthMode }
23387
+ })
23388
+ );
23389
+ }
23390
+ } else if (gatewayAuthMode === "trusted-proxy") {
23391
+ checks.push(
23392
+ toDoctorCheck({
23393
+ id: "state.gatewayAuth",
23394
+ label: "OpenClaw gateway auth",
23395
+ status: "pass",
23396
+ message: "gateway auth is configured with trusted-proxy mode",
23397
+ details: { openclawConfigPath, gatewayAuthMode }
23398
+ })
23399
+ );
23400
+ } else {
23401
+ checks.push(
23402
+ toDoctorCheck({
23403
+ id: "state.gatewayAuth",
23404
+ label: "OpenClaw gateway auth",
23405
+ status: "fail",
23406
+ message: `gateway.auth.mode is missing or unsupported in ${openclawConfigPath}`,
23407
+ remediationHint: OPENCLAW_GATEWAY_AUTH_RECOVERY_HINT,
23408
+ details: { openclawConfigPath }
23409
+ })
23410
+ );
23411
+ }
23267
23412
  } catch {
23268
23413
  checks.push(
23269
23414
  toDoctorCheck({
@@ -23295,6 +23440,16 @@ async function runOpenclawDoctor(options = {}) {
23295
23440
  details: { openclawConfigPath }
23296
23441
  })
23297
23442
  );
23443
+ checks.push(
23444
+ toDoctorCheck({
23445
+ id: "state.gatewayAuth",
23446
+ label: "OpenClaw gateway auth",
23447
+ status: "fail",
23448
+ message: `unable to read ${openclawConfigPath}`,
23449
+ remediationHint: "Ensure the OpenClaw config file exists (OPENCLAW_CONFIG_PATH/CLAWDBOT_CONFIG_PATH, or state dir) and rerun openclaw setup",
23450
+ details: { openclawConfigPath }
23451
+ })
23452
+ );
23298
23453
  }
23299
23454
  const relayRuntimeConfigPath = resolveRelayRuntimeConfigPath(homeDir);
23300
23455
  try {
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawdentity",
3
- "version": "0.0.21",
3
+ "version": "0.0.22",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -21,17 +21,6 @@
21
21
  "postinstall.mjs",
22
22
  "skill-bundle"
23
23
  ],
24
- "scripts": {
25
- "build": "pnpm -F @clawdentity/openclaw-skill build && pnpm run sync:skill-bundle && pnpm run verify:skill-bundle && tsup",
26
- "format": "biome format .",
27
- "lint": "biome lint .",
28
- "prepack": "pnpm run build",
29
- "postinstall": "node ./postinstall.mjs",
30
- "sync:skill-bundle": "node ./scripts/sync-skill-bundle.mjs",
31
- "verify:skill-bundle": "node ./scripts/verify-skill-bundle.mjs",
32
- "test": "vitest run",
33
- "typecheck": "tsc --noEmit"
34
- },
35
24
  "dependencies": {
36
25
  "commander": "^13.1.0",
37
26
  "jsqr": "^1.4.0",
@@ -40,11 +29,21 @@
40
29
  "ws": "^8.19.0"
41
30
  },
42
31
  "devDependencies": {
43
- "@clawdentity/connector": "workspace:*",
44
- "@clawdentity/protocol": "workspace:*",
45
- "@clawdentity/sdk": "workspace:*",
46
32
  "@types/node": "^22.18.11",
47
33
  "@types/pngjs": "^6.0.5",
48
- "@types/qrcode": "^1.5.6"
34
+ "@types/qrcode": "^1.5.6",
35
+ "@clawdentity/connector": "0.0.0",
36
+ "@clawdentity/protocol": "0.0.0",
37
+ "@clawdentity/sdk": "0.0.0"
38
+ },
39
+ "scripts": {
40
+ "build": "pnpm -F @clawdentity/openclaw-skill build && pnpm run sync:skill-bundle && pnpm run verify:skill-bundle && tsup",
41
+ "format": "biome format .",
42
+ "lint": "biome lint .",
43
+ "postinstall": "node ./postinstall.mjs",
44
+ "sync:skill-bundle": "node ./scripts/sync-skill-bundle.mjs",
45
+ "verify:skill-bundle": "node ./scripts/verify-skill-bundle.mjs",
46
+ "test": "vitest run",
47
+ "typecheck": "tsc --noEmit"
49
48
  }
50
- }
49
+ }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: clawdentity_openclaw_relay
3
- description: This skill should be used when the user asks to "set up Clawdentity relay", "pair two agents", "verify an agent token", "rotate API key", "refresh agent auth", "revoke an agent", "troubleshoot relay", "uninstall connector service", or needs OpenClaw relay onboarding, lifecycle management, or pairing workflows.
3
+ description: This skill should be used when the user asks to "set up Clawdentity relay", "pair two agents", "verify an agent token", "rotate API key", "refresh agent auth", "revoke an agent", "troubleshoot relay", "uninstall connector service", "check relay health", "run relay doctor", "test relay connection", "send relay test", "install relay skill", "bootstrap registry", "create onboarding invite", "decommission agent", or needs OpenClaw relay onboarding, lifecycle management, or pairing workflows.
4
4
  version: 0.3.0
5
5
  ---
6
6
 
@@ -104,9 +104,17 @@ Note: Registry operators must run `admin bootstrap` before creating invites. See
104
104
 
105
105
  ### OpenClaw relay setup
106
106
  - `clawdentity skill install`
107
+ - `clawdentity skill install --openclaw-dir <path>`
108
+ - `clawdentity skill install --skill-package-root <path>`
109
+ - `clawdentity skill install --json`
107
110
  - `clawdentity openclaw setup <agent-name>`
108
111
  - `clawdentity openclaw setup <agent-name> --transform-source <path>`
109
112
  - `clawdentity openclaw setup <agent-name> --openclaw-dir <path> --openclaw-base-url <url>`
113
+ - `clawdentity openclaw setup <agent-name> --runtime-mode <auto|service|detached>`
114
+ - `clawdentity openclaw setup <agent-name> --wait-timeout-seconds <seconds>` (default 30)
115
+ - `clawdentity openclaw setup <agent-name> --no-runtime-start`
116
+
117
+ Use `--no-runtime-start` when the connector runs as a separate container or process.
110
118
 
111
119
  ### OpenClaw diagnostics
112
120
  - `clawdentity openclaw doctor`
@@ -143,6 +151,20 @@ Note: Registry operators must run `admin bootstrap` before creating invites. See
143
151
  - `clawdentity admin bootstrap --bootstrap-secret <secret>`
144
152
  - `clawdentity admin bootstrap --bootstrap-secret <secret> --display-name <name> --api-key-name <name> --registry-url <url>`
145
153
 
154
+ ### Command idempotency
155
+
156
+ | Command | Idempotent? | Note |
157
+ |---|---|---|
158
+ | `config init` | Yes | Safe to re-run |
159
+ | `invite redeem` | **No** | One-time; invite consumed on success |
160
+ | `agent create` | No | Fails if agent directory exists |
161
+ | `openclaw setup` | Yes | Primary reconciliation re-entry point |
162
+ | `skill install` | Yes | Reports: installed/updated/unchanged |
163
+ | `pair start` | No | Creates new ticket each time; old ticket expires |
164
+ | `pair confirm` | No | Ticket consumed on success |
165
+ | `connector service install` | Yes | Idempotent |
166
+ | `connector service uninstall` | Yes | Idempotent |
167
+
146
168
  ## Journey (strict order)
147
169
 
148
170
  1. Validate prerequisites.
@@ -178,11 +200,13 @@ Note: Registry operators must run `admin bootstrap` before creating invites. See
178
200
  - `API key saved to local config`
179
201
  - `Human name: <human-name>`
180
202
  - Stop and fix if this step fails. Do not proceed to pairing.
203
+ - **Validate:** `clawdentity config get apiKey` returns a non-empty value.
181
204
 
182
205
  5. Create local OpenClaw agent identity.
183
206
  - Run `clawdentity agent create <agent-name> --framework openclaw`.
184
207
  - Optionally add `--ttl-days <days>` to control token lifetime.
185
208
  - Run `clawdentity agent inspect <agent-name>`.
209
+ - **Validate:** `~/.clawdentity/agents/<agent-name>/ait.jwt` and `secret.key` exist and are non-empty.
186
210
 
187
211
  6. Configure relay setup.
188
212
  - Run:
@@ -197,30 +221,15 @@ Note: Registry operators must run `admin bootstrap` before creating invites. See
197
221
  - runtime mode/status
198
222
  - websocket status `connected`
199
223
  - setup checklist is healthy (fails fast when hook/device/runtime prerequisites drift)
224
+ - **Validate:** run `clawdentity openclaw doctor --json` and confirm all check entries have `status: "pass"`. If any check has `status: "fail"`, use `checkId` to look up remediation in `references/clawdentity-protocol.md` § Doctor Check Reference.
225
+ - If setup throws `CLI_OPENCLAW_SETUP_CHECKLIST_FAILED`, parse `details.firstFailedCheckId` for targeted remediation.
200
226
 
201
227
  7. Validate readiness.
202
- - `clawdentity openclaw setup` already runs an internal checklist and auto-recovers pending OpenClaw gateway device approvals when possible.
228
+ - `clawdentity openclaw setup` already runs an internal checklist, stabilizes OpenClaw gateway auth token mode, and auto-recovers pending OpenClaw gateway device approvals when possible.
203
229
  - Run `clawdentity openclaw doctor` only for diagnostics or CI reporting.
204
230
  - Use `--json` for machine-readable output.
205
231
  - Use `--peer <alias>` to validate a specific peer exists after pairing.
206
- - Doctor check IDs and remediation:
207
-
208
- | Check ID | Validates | Remediation on Failure |
209
- |----------|-----------|----------------------|
210
- | `config.registry` | `registryUrl`, `apiKey`, and `proxyUrl` in config (or proxy env override) | `clawdentity config init` or `invite redeem` |
211
- | `state.selectedAgent` | Agent marker at `~/.clawdentity/openclaw-agent-name` | `clawdentity openclaw setup <agent-name>` |
212
- | `state.credentials` | `ait.jwt` and `secret.key` exist and non-empty | `clawdentity agent create <agent-name>` or `agent auth refresh <agent-name>` |
213
- | `state.peers` | Peers config valid; requested `--peer` alias exists | `clawdentity pair start` / `pair confirm` (optional until pairing) |
214
- | `state.transform` | Relay transform artifacts in OpenClaw hooks dir | Reinstall skill package or `openclaw setup <agent-name>` |
215
- | `state.hookMapping` | `send-to-peer` hook mapping in OpenClaw config | `clawdentity openclaw setup <agent-name>` |
216
- | `state.hookToken` | Hooks enabled with token in OpenClaw config | `clawdentity openclaw setup <agent-name>` then restart OpenClaw |
217
- | `state.hookSessionRouting` | `hooks.defaultSessionKey`, `hooks.allowRequestSessionKey=false`, and required prefixes (`hook:`, default session key) | `clawdentity openclaw setup <agent-name>` then restart OpenClaw |
218
- | `state.gatewayDevicePairing` | Pending OpenClaw device approvals (prevents `pairing required` websocket errors) | Re-run `clawdentity openclaw setup <agent-name>` so setup auto-recovers approvals |
219
- | `state.openclawBaseUrl` | OpenClaw base URL resolvable | `clawdentity openclaw setup <agent-name> --openclaw-base-url <url>` |
220
- | `state.connectorRuntime` | Local connector runtime reachable and websocket-connected | `clawdentity openclaw setup <agent-name>` |
221
- | `state.connectorInboundInbox` | Connector local inbound inbox backlog and replay queue state (`/v1/status`) | Re-run `clawdentity openclaw setup <agent-name>` and verify connector runtime health |
222
- | `state.openclawHookHealth` | Connector replay status for local OpenClaw hook delivery (`/v1/status`) | Re-run `clawdentity openclaw setup <agent-name>` and restart OpenClaw if hook replay stays failed |
223
-
232
+ - Doctor check IDs and remediation are in `references/clawdentity-protocol.md` § Doctor Check Reference.
224
233
  - At this point the agent is ready to start pairing or accept pairing.
225
234
 
226
235
  8. Pairing phase (separate from onboarding).
@@ -246,7 +255,10 @@ Note: Registry operators must run `admin bootstrap` before creating invites. See
246
255
  - If initiator started without `--wait`, initiator must run:
247
256
  - `clawdentity pair status <agent-name> --ticket <clwpair1_...> --wait`
248
257
  - This persists the peer on initiator after responder confirmation.
258
+ - Default wait timeout is 300 seconds with 3-second polling.
259
+ - If `CLI_PAIR_STATUS_WAIT_TIMEOUT` is thrown: the responder did not confirm in time. Recovery: re-run `clawdentity pair start <agent-name> --qr --wait` to generate a new ticket.
249
260
  - Confirm pairing success, then run `clawdentity openclaw relay test`.
261
+ - **Validate:** `~/.clawdentity/peers.json` contains the new peer alias entry.
250
262
 
251
263
  9. Post-pairing verification.
252
264
  - Run `clawdentity verify <path-to-ait.jwt>` to confirm the local agent token is valid.
@@ -257,6 +269,35 @@ Note: Registry operators must run `admin bootstrap` before creating invites. See
257
269
  - `state=queued` is not a pairing failure. The proxy retries delivery automatically while the message is within queue TTL/retry limits.
258
270
  - Note: `relay test` runs preflight doctor checks before sending the probe.
259
271
 
272
+ ## Peer Recognition & Messaging
273
+
274
+ After pairing, agents receive relayed messages from their paired peers.
275
+ Identity injection is enabled by default (`INJECT_IDENTITY_INTO_MESSAGE=true` on proxy).
276
+
277
+ ### Recognizing incoming messages
278
+ - Relayed messages contain a `[Clawdentity Identity]` block prepended to the message body.
279
+ - Extract `agentDid` from this block to identify the sender.
280
+ - See `references/clawdentity-protocol.md` § Identity Injection for the exact block format and field definitions.
281
+ - The connector `deliver` frame also exposes `fromAgentDid` for programmatic access.
282
+
283
+ ### Looking up peer identity
284
+ - Map sender `agentDid` to a known peer by matching against `did` in `~/.clawdentity/peers.json`.
285
+ - Each peer entry contains:
286
+ - `did` — the peer's agent DID (match key)
287
+ - `agentName` — the peer's agent name (use for addressing)
288
+ - `humanName` — the human behind the peer agent (use for context)
289
+ - `proxyUrl` — the peer's proxy endpoint
290
+ - Address the peer by `agentName` or `humanName` in responses for natural conversation.
291
+
292
+ ### Sending messages to a peer
293
+ - Include `"peer": "<alias>"` in the webhook payload to route via relay.
294
+ - The alias is the key in `peers.json` (e.g., `"beta"`).
295
+ - The relay transform strips `peer` from the payload and routes to the connector.
296
+
297
+ ### Peer validation
298
+ - `clawdentity openclaw doctor --peer <alias>` confirms a specific peer is reachable.
299
+ - `clawdentity openclaw relay test --peer <alias>` sends a test probe to the peer.
300
+
260
301
  ## Lifecycle Management
261
302
 
262
303
  ### Token expiry recovery
@@ -287,6 +328,11 @@ Note: Registry operators must run `admin bootstrap` before creating invites. See
287
328
  - Uses cached registry keys (1h TTL) and CRL (15min TTL).
288
329
  - Exit code 1 on verification failure or revocation.
289
330
 
331
+ ### Periodic health checks
332
+ - Run `clawdentity openclaw doctor` periodically to detect stale credentials, expired AIT, or drifted runtime.
333
+ - Run `clawdentity agent inspect <agent-name>` to check token expiry.
334
+ - If AIT is within 24 hours of expiry, proactively run `clawdentity agent auth refresh <agent-name>`.
335
+
290
336
  ## Required Question Policy
291
337
 
292
338
  Ask only when missing:
@@ -308,24 +354,33 @@ Do not suggest switching endpoints unless user explicitly asks for endpoint chan
308
354
  - `CLI_CONNECTOR_MISSING_AGENT_MATERIAL`: agent credentials missing. Rerun `clawdentity agent create <agent-name>` or `clawdentity agent auth refresh <agent-name>`.
309
355
 
310
356
  ### Pairing errors
311
- - `pair start` 403 (`PROXY_PAIR_OWNERSHIP_FORBIDDEN`): initiator ownership check failed. Recreate/refresh the local agent identity.
312
- - `pair start` 503 (`PROXY_PAIR_OWNERSHIP_UNAVAILABLE`): registry ownership validation is unavailable. Check proxy/registry service auth configuration.
313
- - `pair confirm` 404 (`PROXY_PAIR_TICKET_NOT_FOUND`): ticket is invalid or expired. Request a new ticket from initiator.
314
- - `pair confirm` 410 (`PROXY_PAIR_TICKET_EXPIRED`): ticket has expired. Request a new ticket.
357
+ - `PROXY_PAIR_TICKET_NOT_FOUND`: ticket invalid or expired. Request a new ticket from initiator.
358
+ - `PROXY_PAIR_TICKET_EXPIRED`: ticket has expired. Request a new ticket.
359
+ - `CLI_PAIR_STATUS_WAIT_TIMEOUT`: responder did not confirm in time. Re-run `pair start`.
315
360
  - `CLI_PAIR_CONFIRM_INPUT_CONFLICT`: cannot provide both `--ticket` and `--qr-file`. Use one path only.
316
361
  - `CLI_PAIR_PROXY_URL_MISMATCH`: local `proxyUrl` does not match registry metadata. Rerun `clawdentity invite redeem <clw_inv_...>`.
317
362
  - Responder shows peer but initiator does not:
318
363
  - Cause: initiator started pairing without `--wait`.
319
364
  - Fix: run `clawdentity pair status <initiator-agent> --ticket <clwpair1_...> --wait` on initiator.
365
+ - For complete pairing error codes, read `references/clawdentity-protocol.md` § Pairing Error Codes.
320
366
 
321
367
  ### Setup errors
322
368
  - `405 Method Not Allowed` on hook path: rerun `clawdentity openclaw setup <agent-name>` and restart OpenClaw.
323
369
  - `CLI_OPENCLAW_MISSING_AGENT_CREDENTIALS` or `CLI_OPENCLAW_EMPTY_AGENT_CREDENTIALS`: agent credentials missing or empty. Rerun `agent create` or `agent auth refresh`.
370
+ - `CLI_OPENCLAW_SETUP_CHECKLIST_FAILED`: post-setup checklist reported a failing check. Parse `details.firstFailedCheckId` and apply remediation from the doctor check table in `references/clawdentity-protocol.md`. Common failing checks:
371
+ - `state.connectorRuntime` → rerun `openclaw setup <agent-name>`
372
+ - `state.gatewayDevicePairing` → rerun `openclaw setup <agent-name>` (auto-approval)
373
+ - `state.gatewayAuth` → rerun `openclaw setup <agent-name>` (auto-configures gateway auth mode/token)
374
+ - `state.hookToken` → rerun `openclaw setup <agent-name>` then restart OpenClaw
324
375
 
325
376
  ### Credential expiry
326
377
  - Agent AIT expired: run `clawdentity agent auth refresh <agent-name>`, then rerun `clawdentity openclaw setup <agent-name>`.
327
378
  - API key invalid (401 on registry calls): rotate with `api-key create` then `config set apiKey`.
328
379
 
380
+ ### Network connectivity
381
+ - `CLI_PAIR_REQUEST_FAILED` or `CLI_ADMIN_BOOTSTRAP_REQUEST_FAILED`: proxy/registry unreachable. Check DNS, firewall rules, and URL with `clawdentity config show`.
382
+ - If running on an air-gapped machine, confirm proxy/registry URLs resolve to reachable endpoints.
383
+
329
384
  ### General recovery
330
385
  - Report exact missing file/value.
331
386
  - Fix only failing input/config.
@@ -337,7 +392,10 @@ Do not suggest switching endpoints unless user explicitly asks for endpoint chan
337
392
 
338
393
  | File | Purpose |
339
394
  |------|---------|
340
- | `references/clawdentity-protocol.md` | Peer-map schema, pairing contract, connector handoff envelope, proxy URL resolution, pairing error codes, cache files, peer alias derivation |
341
- | `references/clawdentity-registry.md` | Admin bootstrap, API key lifecycle, agent revocation, auth refresh |
395
+ | `references/clawdentity-protocol.md` | Peer-map schema, pairing contract, connector handoff, error codes, Docker guidance, doctor checks, identity injection |
396
+ | `references/clawdentity-registry.md` | Admin bootstrap, API key lifecycle, agent revocation, auth refresh, connector errors |
397
+ | `references/clawdentity-environment.md` | Complete environment variable reference for all CLI overrides |
398
+ | `examples/peers-sample.json` | Valid peers.json example with one peer entry |
399
+ | `examples/openclaw-relay-sample.json` | Relay runtime config example |
342
400
 
343
401
  Directive: read the reference files before troubleshooting relay contract, connector handoff failures, or registry/admin operations.
@@ -0,0 +1,5 @@
1
+ {
2
+ "openclawBaseUrl": "http://127.0.0.1:18789",
3
+ "openclawHookToken": "<auto-provisioned-token>",
4
+ "updatedAt": "2026-02-15T20:00:00.000Z"
5
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "peers": {
3
+ "beta": {
4
+ "did": "did:claw:agent:01HEXAMPLE",
5
+ "proxyUrl": "https://proxy.clawdentity.com/hooks/agent",
6
+ "agentName": "beta",
7
+ "humanName": "Ira"
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,40 @@
1
+ # Clawdentity Environment Variable Reference
2
+
3
+ ## Purpose
4
+
5
+ Complete reference for CLI environment variable overrides. When env overrides are present, config-file URL mismatches are not blockers.
6
+
7
+ ## CLI Environment Variables
8
+
9
+ | Variable | Purpose | Used By |
10
+ |---|---|---|
11
+ | `CLAWDENTITY_PROXY_URL` | Override proxy URL | pair, connector |
12
+ | `CLAWDENTITY_PROXY_WS_URL` | Override proxy WebSocket URL | connector |
13
+ | `CLAWDENTITY_REGISTRY_URL` | Override registry URL | config |
14
+ | `CLAWDENTITY_CONNECTOR_BASE_URL` | Override connector bind URL | connector |
15
+ | `CLAWDENTITY_CONNECTOR_OUTBOUND_PATH` | Override outbound path | relay transform |
16
+ | `CLAWDENTITY_AGENT_NAME` | Override agent name resolution | openclaw, transform |
17
+ | `OPENCLAW_BASE_URL` | Override OpenClaw upstream URL | openclaw setup |
18
+ | `OPENCLAW_HOOK_TOKEN` | Override hook auth token | openclaw setup |
19
+ | `OPENCLAW_GATEWAY_TOKEN` | Override gateway auth token | openclaw setup |
20
+ | `OPENCLAW_CONFIG_PATH` | Override OpenClaw config file path | openclaw |
21
+ | `OPENCLAW_STATE_DIR` | Override OpenClaw state directory | openclaw |
22
+ | `OPENCLAW_HOME` | Override OpenClaw home directory (used when explicit config/state overrides are unset) | openclaw |
23
+
24
+ ## Legacy Environment Variables
25
+
26
+ | Variable | Replaced By |
27
+ |---|---|
28
+ | `CLAWDBOT_CONFIG_PATH` | `OPENCLAW_CONFIG_PATH` |
29
+ | `CLAWDBOT_STATE_DIR` | `OPENCLAW_STATE_DIR` |
30
+
31
+ ## Proxy Server Environment Variables
32
+
33
+ These variables configure the Clawdentity proxy server (operator-facing, not CLI):
34
+
35
+ | Variable | Purpose | Default |
36
+ |---|---|---|
37
+ | `INJECT_IDENTITY_INTO_MESSAGE` | Enable/disable identity block injection into relayed messages | `true` |
38
+ | `RELAY_QUEUE_MAX_MESSAGES_PER_AGENT` | Max queued messages per agent | `500` |
39
+ | `RELAY_QUEUE_TTL_SECONDS` | Queue message time-to-live | `3600` |
40
+ | `RELAY_RETRY_INITIAL_MS` | Initial retry delay for relay delivery | `1000` |
@@ -6,31 +6,7 @@ Define the exact runtime contract used by `relay-to-peer.mjs`.
6
6
 
7
7
  ## Filesystem Paths
8
8
 
9
- ### OpenClaw files
10
- - `<resolved-openclaw-state>/openclaw.json` (legacy filenames may exist: `clawdbot.json`, `moldbot.json`, `moltbot.json`)
11
- - `<resolved-openclaw-state>/hooks/transforms/relay-to-peer.mjs`
12
- - `<resolved-openclaw-state>/hooks/transforms/clawdentity-relay.json`
13
- - `<resolved-openclaw-state>/hooks/transforms/clawdentity-peers.json`
14
- - `<resolved-openclaw-state>/skills/clawdentity-openclaw-relay/SKILL.md`
15
- - env overrides:
16
- - `OPENCLAW_CONFIG_PATH`, `CLAWDBOT_CONFIG_PATH`
17
- - `OPENCLAW_STATE_DIR`, `CLAWDBOT_STATE_DIR`
18
- - `OPENCLAW_HOME` (used when explicit config/state overrides are unset)
19
-
20
- ### Clawdentity files
21
- - `~/.clawdentity/config.json`
22
- - `~/.clawdentity/agents/<agent-name>/secret.key`
23
- - `~/.clawdentity/agents/<agent-name>/public.key`
24
- - `~/.clawdentity/agents/<agent-name>/identity.json`
25
- - `~/.clawdentity/agents/<agent-name>/registry-auth.json`
26
- - `~/.clawdentity/agents/<agent-name>/ait.jwt`
27
- - `~/.clawdentity/peers.json`
28
- - `~/.clawdentity/openclaw-agent-name`
29
- - `~/.clawdentity/openclaw-relay.json`
30
- - `~/.clawdentity/openclaw-connectors.json`
31
- - `~/.clawdentity/pairing/` (ephemeral QR PNG storage, auto-cleaned after 900s)
32
- - `~/.clawdentity/cache/registry-keys.json` (1-hour TTL, used by `verify`)
33
- - `~/.clawdentity/cache/crl-claims.json` (15-minute TTL, used by `verify`)
9
+ Canonical paths are defined in SKILL.md § Filesystem Truth. Refer there for all path contracts.
34
10
 
35
11
  ## Setup Input Contract
36
12
 
@@ -214,32 +190,82 @@ Known defaults:
214
190
 
215
191
  Recovery: rerun onboarding (`clawdentity invite redeem <clw_inv_...> --display-name <human-name>`) so local config aligns to registry metadata.
216
192
 
193
+ ## Identity Injection
194
+
195
+ When identity injection is enabled (proxy env `INJECT_IDENTITY_INTO_MESSAGE`, default `true`), the proxy prepends an identity block to the `message` field of relayed payloads.
196
+
197
+ ### Block format
198
+
199
+ ```
200
+ [Clawdentity Identity]
201
+ agentDid: did:claw:agent:01H...
202
+ ownerDid: did:claw:human:01H...
203
+ issuer: https://registry.clawdentity.com
204
+ aitJti: 01H...
205
+ ```
206
+
207
+ The block is separated from the original message by a blank line (`\n\n`).
208
+
209
+ ### Field definitions
210
+
211
+ | Field | Description |
212
+ |---|---|
213
+ | `agentDid` | Sender agent DID — use to identify the peer |
214
+ | `ownerDid` | DID of the human who owns the sender agent |
215
+ | `issuer` | Registry URL that issued the sender's AIT |
216
+ | `aitJti` | Unique JTI claim from the sender's AIT |
217
+
218
+ ### Programmatic access
219
+
220
+ The connector `deliver` frame includes `fromAgentDid` as a top-level field. Inbound inbox items (`ConnectorInboundInboxItem`) also expose `fromAgentDid` for programmatic sender identification without parsing the identity block.
221
+
217
222
  ## Pairing Error Codes
218
223
 
219
224
  ### `pair start` errors
220
225
 
221
- | HTTP Status | Error Code | Meaning |
222
- |-------------|-----------|---------|
223
- | 403 | `PROXY_PAIR_OWNERSHIP_FORBIDDEN` | Initiator ownership check failed |
224
- | 503 | `PROXY_PAIR_OWNERSHIP_UNAVAILABLE` | Registry ownership lookup unavailable |
225
- | — | `CLI_PAIR_AGENT_NOT_FOUND` | Agent ait.jwt or secret.key missing/empty |
226
- | — | `CLI_PAIR_HUMAN_NAME_MISSING` | Local config is missing `humanName`; set via invite redeem or config |
227
- | — | `CLI_PAIR_PROXY_URL_REQUIRED` | Proxy URL could not be resolved |
228
- | — | `CLI_PAIR_START_INVALID_TTL` | ttlSeconds must be a positive integer |
229
- | — | `CLI_PAIR_INVALID_PROXY_URL` | Proxy URL is invalid |
230
- | — | `CLI_PAIR_REQUEST_FAILED` | Unable to connect to proxy URL |
226
+ | HTTP Status | Error Code | Meaning | Recovery |
227
+ |---|---|---|---|
228
+ | 403 | `PROXY_PAIR_OWNERSHIP_FORBIDDEN` | Initiator ownership check failed | Recreate/refresh the local agent identity |
229
+ | 503 | `PROXY_PAIR_OWNERSHIP_UNAVAILABLE` | Registry ownership lookup unavailable | Check proxy/registry service auth configuration |
230
+ | — | `CLI_PAIR_AGENT_NOT_FOUND` | Agent ait.jwt or secret.key missing/empty | Run `agent create` or `agent auth refresh` |
231
+ | — | `CLI_PAIR_HUMAN_NAME_MISSING` | Local config is missing `humanName` | Set via `invite redeem` or config |
232
+ | — | `CLI_PAIR_PROXY_URL_REQUIRED` | Proxy URL could not be resolved | Run `invite redeem` or set `CLAWDENTITY_PROXY_URL` |
233
+ | — | `CLI_PAIR_START_INVALID_TTL` | ttlSeconds must be a positive integer | Use valid `--ttl-seconds` value |
234
+ | — | `CLI_PAIR_INVALID_PROXY_URL` | Proxy URL is invalid | Fix proxy URL in config |
235
+ | — | `CLI_PAIR_REQUEST_FAILED` | Unable to connect to proxy URL | Check DNS, firewall, proxy URL |
236
+ | — | `CLI_PAIR_START_FAILED` | Generic pair start failure | Retry; check proxy connectivity |
237
+ | — | `CLI_PAIR_PROFILE_INVALID` | Name too long, contains control characters, or empty | Fix agent or human name |
231
238
 
232
239
  ### `pair confirm` errors
233
240
 
234
- | HTTP Status | Error Code | Meaning |
235
- |-------------|-----------|---------|
236
- | 404 | `PROXY_PAIR_TICKET_NOT_FOUND` | Pairing ticket is invalid or expired |
237
- | 410 | `PROXY_PAIR_TICKET_EXPIRED` | Pairing ticket has expired |
238
- | — | `CLI_PAIR_CONFIRM_TICKET_REQUIRED` | Either --ticket or --qr-file is required |
239
- | — | `CLI_PAIR_CONFIRM_INPUT_CONFLICT` | Cannot provide both --ticket and --qr-file |
240
- | — | `CLI_PAIR_CONFIRM_TICKET_INVALID` | Pairing ticket is invalid |
241
- | — | `CLI_PAIR_CONFIRM_QR_FILE_NOT_FOUND` | QR file not found |
242
- | — | `CLI_PAIR_CONFIRM_QR_NOT_FOUND` | No pairing QR code found in image |
241
+ | HTTP Status | Error Code | Meaning | Recovery |
242
+ |---|---|---|---|
243
+ | 404 | `PROXY_PAIR_TICKET_NOT_FOUND` | Pairing ticket is invalid or expired | Request new ticket from initiator |
244
+ | 410 | `PROXY_PAIR_TICKET_EXPIRED` | Pairing ticket has expired | Request new ticket |
245
+ | — | `CLI_PAIR_CONFIRM_TICKET_REQUIRED` | Either --ticket or --qr-file is required | Provide one input path |
246
+ | — | `CLI_PAIR_CONFIRM_INPUT_CONFLICT` | Cannot provide both --ticket and --qr-file | Use one input path only |
247
+ | — | `CLI_PAIR_CONFIRM_TICKET_INVALID` | Pairing ticket is invalid | Get new ticket from initiator |
248
+ | — | `CLI_PAIR_CONFIRM_QR_FILE_NOT_FOUND` | QR file not found | Verify file path |
249
+ | — | `CLI_PAIR_CONFIRM_QR_NOT_FOUND` | No pairing QR code found in image | Request new QR from initiator |
250
+ | — | `CLI_PAIR_CONFIRM_FAILED` | Generic pair confirm failure | Retry with new ticket |
251
+ | — | `CLI_PAIR_CONFIRM_QR_FILE_INVALID` | QR image file corrupt or unsupported | Request new QR from initiator |
252
+ | — | `CLI_PAIR_CONFIRM_QR_FILE_REQUIRED` | QR path unusable | Verify file path and format |
253
+
254
+ ### `pair status` errors
255
+
256
+ | HTTP Status | Error Code | Meaning | Recovery |
257
+ |---|---|---|---|
258
+ | — | `CLI_PAIR_STATUS_FAILED` | Generic pair status failure | Retry |
259
+ | — | `CLI_PAIR_STATUS_WAIT_TIMEOUT` | Wait polling timed out | Generate new ticket via `pair start` |
260
+ | — | `CLI_PAIR_STATUS_FORBIDDEN` | 403 on status check — ownership mismatch | Verify correct agent |
261
+ | — | `CLI_PAIR_STATUS_TICKET_REQUIRED` | Missing ticket argument | Provide `--ticket <clwpair1_...>` |
262
+
263
+ ### Peer persistence errors
264
+
265
+ | Error Code | Meaning | Recovery |
266
+ |---|---|---|
267
+ | `CLI_PAIR_PEERS_CONFIG_INVALID` | `peers.json` corrupt or invalid structure | Delete `peers.json` and re-pair |
268
+ | `CLI_PAIR_PEER_ALIAS_INVALID` | Derived alias fails validation | Re-pair with valid agent DID |
243
269
 
244
270
  ## Cache Files
245
271
 
@@ -261,3 +287,38 @@ When `pair confirm` saves a new peer, alias is derived automatically:
261
287
  5. Fallback alias is `peer` if DID is not a valid agent DID.
262
288
 
263
289
  Alias validation: `[a-zA-Z0-9._-]`, max 128 characters.
290
+
291
+ ## Container Environments
292
+
293
+ When running in Docker or similar container runtimes:
294
+
295
+ - `openclaw setup` writes Docker-aware endpoint candidates into `clawdentity-relay.json`:
296
+ - `host.docker.internal`, `gateway.docker.internal`, Linux bridge (`172.17.0.1`), default gateway, and loopback.
297
+ - Candidates are attempted in order by the relay transform.
298
+ - Use `--no-runtime-start` when the connector runs as a separate container or process.
299
+ - Required env overrides for container networking:
300
+ - `OPENCLAW_BASE_URL` — point to OpenClaw inside/outside the container network.
301
+ - `CLAWDENTITY_CONNECTOR_BASE_URL` — point to the connector's bind address from the transform's perspective.
302
+ - Port allocation: each agent gets its own connector port starting from `19400`.
303
+ - Port assignment is tracked in `~/.clawdentity/openclaw-connectors.json`.
304
+
305
+ ## Doctor Check Reference
306
+
307
+ Run `clawdentity openclaw doctor --json` for machine-readable diagnostics.
308
+
309
+ | Check ID | Validates | Remediation on Failure |
310
+ |---|---|---|
311
+ | `config.registry` | `registryUrl`, `apiKey`, and `proxyUrl` in config (or proxy env override) | `clawdentity config init` or `invite redeem` |
312
+ | `state.selectedAgent` | Agent marker at `~/.clawdentity/openclaw-agent-name` | `clawdentity openclaw setup <agent-name>` |
313
+ | `state.credentials` | `ait.jwt` and `secret.key` exist and non-empty | `clawdentity agent create <agent-name>` or `agent auth refresh <agent-name>` |
314
+ | `state.peers` | Peers config valid; requested `--peer` alias exists | `clawdentity pair start` / `pair confirm` (optional until pairing) |
315
+ | `state.transform` | Relay transform artifacts in OpenClaw hooks dir | Reinstall skill package or `openclaw setup <agent-name>` |
316
+ | `state.hookMapping` | `send-to-peer` hook mapping in OpenClaw config | `clawdentity openclaw setup <agent-name>` |
317
+ | `state.hookToken` | Hooks enabled with token in OpenClaw config | `clawdentity openclaw setup <agent-name>` then restart OpenClaw |
318
+ | `state.hookSessionRouting` | `hooks.defaultSessionKey`, `hooks.allowRequestSessionKey=false`, and required prefixes | `clawdentity openclaw setup <agent-name>` then restart OpenClaw |
319
+ | `state.gatewayAuth` | OpenClaw `gateway.auth` readiness (`mode` + required credential) | `clawdentity openclaw setup <agent-name>` to re-sync gateway auth |
320
+ | `state.gatewayDevicePairing` | Pending OpenClaw device approvals | Re-run `clawdentity openclaw setup <agent-name>` so setup auto-recovers approvals |
321
+ | `state.openclawBaseUrl` | OpenClaw base URL resolvable | `clawdentity openclaw setup <agent-name> --openclaw-base-url <url>` |
322
+ | `state.connectorRuntime` | Local connector runtime reachable and websocket-connected | `clawdentity openclaw setup <agent-name>` |
323
+ | `state.connectorInboundInbox` | Connector local inbound inbox backlog and replay queue state | Re-run `clawdentity openclaw setup <agent-name>` and verify connector runtime health |
324
+ | `state.openclawHookHealth` | Connector replay status for local OpenClaw hook delivery | Re-run `clawdentity openclaw setup <agent-name>` and restart OpenClaw if hook replay stays failed |
@@ -173,3 +173,14 @@ Admin-only. Creates a registry invite code (`clw_inv_...`) for onboarding new us
173
173
  | 401 | Authentication failed |
174
174
  | 403 | Requires admin access |
175
175
  | 400 | Invalid request |
176
+
177
+ ## Connector Errors
178
+
179
+ | Error Code | Meaning | Recovery |
180
+ |---|---|---|
181
+ | `CLI_CONNECTOR_SERVICE_PLATFORM_INVALID` | Invalid platform argument | Use `auto`, `launchd`, or `systemd` |
182
+ | `CLI_CONNECTOR_SERVICE_PLATFORM_UNSUPPORTED` | OS unsupported for selected platform | Use a supported platform (macOS: launchd, Linux: systemd) |
183
+ | `CLI_CONNECTOR_SERVICE_INSTALL_FAILED` | Service install failed | Check permissions, systemd/launchd status |
184
+ | `CLI_CONNECTOR_PROXY_URL_REQUIRED` | Proxy URL unresolvable | Run `invite redeem` or set `CLAWDENTITY_PROXY_URL` / `CLAWDENTITY_PROXY_WS_URL` |
185
+ | `CLI_CONNECTOR_INVALID_REGISTRY_AUTH` | `registry-auth.json` corrupt or invalid | Run `clawdentity agent auth refresh <agent-name>` |
186
+ | `CLI_CONNECTOR_INVALID_AGENT_IDENTITY` | `identity.json` corrupt or invalid | Re-create agent with `clawdentity agent create <agent-name>` |