{"record":{"id":"a676623fe2522f9e","repo":"JuliusBrussee/caveman","slug":"remote-content-not-enabled","errorCode":"remote_content_not_enabled","errorMessage":"remote_content_not_enabled","messagePattern":"remote_content_not_enabled","errorType":"error_code","errorClass":"MiddlewareError","httpStatus":null,"severity":"error","filePath":"packages/sdk/typescript/src/middleware/runtime.ts","lineNumber":96,"sourceCode":"  private readonly options: RuntimeOptions;\n  private readonly fetcher: typeof globalThis.fetch;\n  private readonly lifetime = new AbortController();\n  private readonly bindings = new WeakSet<RecoveryBinding>();\n  private readonly capsCache: { value: Capabilities | null } = { value: null };\n  private failures = 0;\n  private openUntil = 0;\n  private pending = 0;\n  private receiptsPending = 0;\n  private fetchesPending = 0;\n  private receiptFetchesPending = 0;\n  private receiptTail: Promise<void> = Promise.resolve();\n  private reported: CallReport | null = null;\n\n  constructor(options: RuntimeOptions = {}) {\n    const url = new URL(options.endpoint ?? 'http://127.0.0.1:8787');\n    const local = ['127.0.0.1','[::1]','localhost'].includes(url.hostname);\n    if (!['http:','https:'].includes(url.protocol) || url.username || url.password || url.search || url.hash || (url.pathname !== '/' && url.pathname !== '')) throw new MiddlewareError('invalid_endpoint');\n    if (!local && (!options.allowRemoteContent || url.protocol !== 'https:')) throw new MiddlewareError('remote_content_not_enabled');\n    for (const value of [options.deadlineMs, options.retrieveDeadlineMs]) {\n      if (value !== undefined && (!Number.isSafeInteger(value) || value <= 0)) throw new MiddlewareError('invalid_deadline');\n    }\n    this.endpoint = url.origin;\n    this.options = { ...options };\n    this.mode = options.mode ?? 'compress';\n    this.fetcher = options.fetch ?? globalThis.fetch;\n  }\n\n  /** Prime capability discovery during app startup, outside the first model call. */\n  async ready(signal?: AbortSignal): Promise<Capabilities> {\n    signal?.throwIfAborted();\n    if (this.mode === 'off') throw new MiddlewareError('off');\n    const value = validateCapabilities(await this.http('capabilities', undefined, this.options.deadlineMs ?? 100, signal));\n    this.capsCache.value = value;\n    return value;\n  }\n","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/sdk/typescript/src/middleware/runtime.ts#L78-L114","documentation":"For non-local endpoints, the Runtime constructor requires explicit opt-in to loading remote content: the endpoint hostname must be localhost/127.0.0.1/[::1] to be exempt, otherwise options.allowRemoteContent must be true AND the protocol must be https:. If either condition fails, MiddlewareError with code 'remote_content_not_enabled' is thrown to prevent silently sending code/content over plaintext or to unexpected remote hosts.","triggerScenarios":"new Runtime({ endpoint: 'https://cave.example.com' }) without allowRemoteContent: true; or new Runtime({ endpoint: 'http://cave.example.com', allowRemoteContent: true }) — plain-text http to a remote host is always rejected regardless of the flag. Only https + explicit opt-in passes for remote endpoints.","commonSituations":"Deploying the middleware against a staging/production Cave host while keeping local-dev options; configuring http:// for a remote VM during testing; forgetting the allowRemoteContent flag when the endpoint moved from localhost to a remote host in CI.","solutions":["Add allowRemoteContent: true to the Runtime options when targeting a remote host","Ensure the remote endpoint uses https:// (never http:// for non-local hosts)","Keep using http://127.0.0.1:8787 (or localhost/[::1]) for local development where the flag is unnecessary","If this fires in CI, check which environment's endpoint the config resolved to"],"exampleFix":"// before\nconst rt = new Runtime({ endpoint: 'https://cave.example.com' });\n// after\nconst rt = new Runtime({ endpoint: 'https://cave.example.com', allowRemoteContent: true });","handlingStrategy":"validation","validationCode":"const u = new URL(endpoint);\nconst local = ['127.0.0.1', '[::1]', 'localhost'].includes(u.hostname);\nif (!local && !(allowRemoteContent && u.protocol === 'https:')) throw new Error('remote endpoints require https and allowRemoteContent: true');","typeGuard":"function remoteEndpointAllowed(endpoint: string, allowRemoteContent: boolean): boolean { const u = new URL(endpoint); const local = ['127.0.0.1', '[::1]', 'localhost'].includes(u.hostname); return local || (allowRemoteContent && u.protocol === 'https:'); }","tryCatchPattern":"try { const rt = new Runtime({ endpoint, allowRemoteContent }); } catch (e) { if (e instanceof MiddlewareError && e.code === 'remote_content_not_enabled') throw new Error('set allowRemoteContent: true and use https:// for remote middleware endpoints'); throw e; }","preventionTips":["Set allowRemoteContent: true explicitly whenever the endpoint host is not localhost","Always use https:// for remote endpoints; treat http:// remote as a config bug","Keep local dev on http://127.0.0.1:8787 to avoid needing the flag","Audit CI config so staging/prod endpoints include the opt-in flag"],"tags":["security","config","middleware","https"],"backgroundTag":"feature-not-enabled","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}