{"record":{"id":"62f5001eabc9615c","repo":"can1357/oh-my-pi","slug":"imageurls-exposure-ssh-requires-imageurls-public","errorCode":null,"errorMessage":"imageUrls exposure \"ssh\" requires imageUrls.publicBaseUrl","messagePattern":"imageUrls exposure \"ssh\" requires imageUrls\\.publicBaseUrl","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/blob-broker/exposure.ts","lineNumber":488,"sourceCode":"\t\t\t\tconst configFile = optionString(config, \"configFile\");\n\t\t\t\tconst tunnelName = optionString(config, \"tunnelName\");\n\t\t\t\tif (!configFile || !tunnelName) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t'imageUrls exposure \"named-cloudflared\" requires credentials.tunnelToken or options.configFile and options.tunnelName',\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\targv = [binary, \"tunnel\", \"--no-autoupdate\", \"--config\", configFile, \"run\", tunnelName];\n\t\t\t}\n\t\t\tconst baseUrl = normalizeBaseUrl(config.publicBaseUrl);\n\t\t\tconst { proc } = await spawnUrlTunnel(\n\t\t\t\targv,\n\t\t\t\t() => baseUrl,\n\t\t\t\t/Registered tunnel connection|Connection [a-z0-9-]+ registered/i,\n\t\t\t);\n\t\t\treturn processExposure(\"named-cloudflared\", baseUrl, proc);\n\t\t}\n\t\tcase \"ssh\": {\n\t\t\tif (!config.publicBaseUrl) throw new Error('imageUrls exposure \"ssh\" requires imageUrls.publicBaseUrl');\n\t\t\tif (!config.sshTarget) throw new Error('imageUrls exposure \"ssh\" requires imageUrls.sshTarget');\n\t\t\tconst binary = requireBinary(\"ssh\");\n\t\t\tconst remotePort = config.sshRemotePort ?? 8787;\n\t\t\tconst proc = Bun.spawn(\n\t\t\t\t[\n\t\t\t\t\tbinary,\n\t\t\t\t\t\"-o\",\n\t\t\t\t\t\"BatchMode=yes\",\n\t\t\t\t\t\"-o\",\n\t\t\t\t\t\"ExitOnForwardFailure=yes\",\n\t\t\t\t\t\"-N\",\n\t\t\t\t\t\"-R\",\n\t\t\t\t\t`${remotePort}:127.0.0.1:${port}`,\n\t\t\t\t\tconfig.sshTarget,\n\t\t\t\t],\n\t\t\t\t{ env: process.env, stdin: \"ignore\", stdout: \"ignore\", stderr: \"ignore\", cwd: os.homedir() },\n\t\t\t);\n\t\t\tconst early = await Promise.race([","sourceCodeStart":470,"sourceCodeEnd":506,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/blob-broker/exposure.ts#L470-L506","documentation":"The ssh exposure performs a reverse forward (ssh -R) to a remote host; a fronting web server on that host serves the blobs, and omp must know its public origin. This error throws when config.publicBaseUrl is absent for kind \"ssh\". The URL cannot be derived from the ssh target alone because the fronting server may use any scheme/host.","triggerScenarios":"Selecting exposure kind \"ssh\" without setting imageUrls.publicBaseUrl in the config.","commonSituations":"User configured sshTarget/sshRemotePort but forgot publicBaseUrl; assumed the ssh host is auto-detected; copied a tunnel-kind config and switched kind to ssh.","solutions":["Add imageUrls.publicBaseUrl, e.g. \"https://myserver.example.com\" or \"http://203.0.113.5:8787\".","Ensure the remote web server actually fronts that origin at sshRemotePort (default 8787).","Verify no trailing slash in publicBaseUrl."],"exampleFix":"// before\n\"imageUrls\": { \"exposure\": { \"kind\": \"ssh\", \"sshTarget\": \"user@host\" } }\n// after\n\"imageUrls\": { \"publicBaseUrl\": \"https://host.example.com\", \"exposure\": { \"kind\": \"ssh\", \"sshTarget\": \"user@host\" } }","handlingStrategy":"validation","validationCode":"if (config.kind === \"ssh\") {\n  if (typeof config.publicBaseUrl !== \"string\" || !/^https?:\\/\\/[^/]+$/.test(config.publicBaseUrl)) {\n    throw new Error(\"ssh exposure needs publicBaseUrl of the fronting web server\");\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Set publicBaseUrl whenever the exposure kind is ssh","Confirm the remote fronting server actually serves that origin at sshRemotePort","Validate exposure config at startup, before spawning ssh"],"tags":["configuration","ssh","missing-required-option"],"backgroundTag":"missing-required-option","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}