Repository navigation
service add / deploy: --endpoint udp|tcp+udp, and show a udp service's udp:// address - #375
Conversation
…e's udp:// address The compute plane serves public UDP on its v6 lane in every region (live 2026-10-14) and instacloud-platform#681 widens `endpoint` to http | tcp | udp | tcp+udp. This is the CLI half, mirroring #368 (tcp): - `--endpoint` on `service add` and `deploy` accepts udp and tcp+udp, parsed locally (a typo or `udp+tcp` fails before any network call) and sent only when given. Port rules stay the platform's answer, as for tcp. - The archive lane's body carries the new values. - `service list` / the added line render udp as udp://host:port and tcp+udp as `tcp://host:port, udp://host:port` (or `<proto>/<port> (address after the first deploy)` before a deploy confirms the host). tcp and http rows are byte-for-byte unchanged. - `deploy` prints the scheme from --endpoint; the deploy result names the host but not the protocol, so a v6 redeploy without --endpoint prints `host:port (endpoint unchanged; ...)` instead of guessing tcp://. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
3 issues found across 6 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/index.ts">
<violation number="1" location="src/index.ts:553">
P2: `deploy --help` enables UDP without warning that a wake-held datagram is re-sent until the service replies, so an app may process it twice; receive-only services can also sleep. Add the UDP replay and `--always-on` caveats here so users switching endpoints through deploy see these operational constraints.</violation>
</file>
<file name="src/commands/deploy.ts">
<violation number="1" location="src/commands/deploy.ts:38">
P2: An explicit `--endpoint http` is treated as “endpoint unchanged” here, and any returned `url` is ignored when `endpointHost` is present. Prefer the HTTP URL for `http`; reserve this fallback for an omitted endpoint.</violation>
</file>
<file name="src/commands/services.ts">
<violation number="1" location="src/commands/services.ts:148">
P3: The UDP/tcp+udp address can print `:undefined` or `:null` when the service response has a host but no port. Use the same `s.port ?? '?'` fallback as the no-host branch.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
| .option('--image <url>', 'prebuilt container image to deploy (instead of a source dir)').option('--branch <b>').option('--group <g>').option('--port <p>', 'port the app listens on (0 for a worker without a routed port)') | ||
| .option('--websocket', 'run a WebSocket app (larger guest + connection-based concurrency)') | ||
| .option('--endpoint <http|tcp>', 'switch how the service is reached from this deploy on: tcp (raw TCP on --port at its own public IPv6 address; IPv6 clients only) or http (back to its HTTPS domain). Omitted: keep the current endpoint') | ||
| .option('--endpoint <http|tcp|udp|tcp+udp>', 'switch how the service is reached from this deploy on: tcp / udp / tcp+udp (raw TCP, UDP or both on --port at its own public IPv6 address; IPv6 clients only) or http (back to its HTTPS domain). Omitted: keep the current endpoint') |
There was a problem hiding this comment.
P2: deploy --help enables UDP without warning that a wake-held datagram is re-sent until the service replies, so an app may process it twice; receive-only services can also sleep. Add the UDP replay and --always-on caveats here so users switching endpoints through deploy see these operational constraints.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/index.ts, line 553:
<comment>`deploy --help` enables UDP without warning that a wake-held datagram is re-sent until the service replies, so an app may process it twice; receive-only services can also sleep. Add the UDP replay and `--always-on` caveats here so users switching endpoints through deploy see these operational constraints.</comment>
<file context>
@@ -550,7 +550,7 @@ buildCmd.command('logs <build-id>').description('Read source-build output for a
.option('--image <url>', 'prebuilt container image to deploy (instead of a source dir)').option('--branch <b>').option('--group <g>').option('--port <p>', 'port the app listens on (0 for a worker without a routed port)')
.option('--websocket', 'run a WebSocket app (larger guest + connection-based concurrency)')
- .option('--endpoint <http|tcp>', 'switch how the service is reached from this deploy on: tcp (raw TCP on --port at its own public IPv6 address; IPv6 clients only) or http (back to its HTTPS domain). Omitted: keep the current endpoint')
+ .option('--endpoint <http|tcp|udp|tcp+udp>', 'switch how the service is reached from this deploy on: tcp / udp / tcp+udp (raw TCP, UDP or both on --port at its own public IPv6 address; IPv6 clients only) or http (back to its HTTPS domain). Omitted: keep the current endpoint')
.option('--replace-source', 'the service deploys from a connected GitHub repo: switch it to this image and remove the repo connection (admin); without it such a deploy is refused')
.option('--json', 'print the deploy result as JSON (build progress goes to stderr)')
</file context>
| .option('--endpoint <http|tcp|udp|tcp+udp>', 'switch how the service is reached from this deploy on: tcp / udp / tcp+udp (raw TCP, UDP or both on --port at its own public IPv6 address; IPv6 clients only) or http (back to its HTTPS domain). Omitted: keep the current endpoint') | |
| .option('--endpoint <http|tcp|udp|tcp+udp>', 'switch how the service is reached from this deploy on: tcp / udp / tcp+udp (raw TCP, UDP or both on --port at its own public IPv6 address; IPv6 clients only) or http (back to its HTTPS domain). Omitted: keep the current endpoint. UDP wake-held datagrams are re-sent until the service first replies (an app may see them twice); receive-only UDP services need --always-on') |
| // guessing tcp for what may be a udp service. Pure, so it's unit-tested. | ||
| export function deployedTarget(r: { url?: string; endpointHost?: string | null }, port: string | undefined, endpoint?: Endpoint): string { | ||
| if (r.endpointHost) { | ||
| if (endpoint === undefined || endpoint === 'http') return `${r.endpointHost}:${port ?? '?'} (endpoint unchanged; \`insta service list\` shows its protocol)` |
There was a problem hiding this comment.
P2: An explicit --endpoint http is treated as “endpoint unchanged” here, and any returned url is ignored when endpointHost is present. Prefer the HTTP URL for http; reserve this fallback for an omitted endpoint.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/commands/deploy.ts, line 38:
<comment>An explicit `--endpoint http` is treated as “endpoint unchanged” here, and any returned `url` is ignored when `endpointHost` is present. Prefer the HTTP URL for `http`; reserve this fallback for an omitted endpoint.</comment>
<file context>
@@ -28,10 +28,16 @@ export function deployRequestBody(source: { image: string }, branch: string, opt
+// guessing tcp for what may be a udp service. Pure, so it's unit-tested.
+export function deployedTarget(r: { url?: string; endpointHost?: string | null }, port: string | undefined, endpoint?: Endpoint): string {
+ if (r.endpointHost) {
+ if (endpoint === undefined || endpoint === 'http') return `${r.endpointHost}:${port ?? '?'} (endpoint unchanged; \`insta service list\` shows its protocol)`
+ return (endpoint === 'tcp+udp' ? ['tcp', 'udp'] : [endpoint]).map((pr) => `${pr}://${r.endpointHost}:${port ?? '?'}`).join(', ')
+ }
</file context>
| if (endpoint === undefined || endpoint === 'http') return `${r.endpointHost}:${port ?? '?'} (endpoint unchanged; \`insta service list\` shows its protocol)` | |
| if (endpoint === undefined) return `${r.endpointHost}:${port ?? '?'} (endpoint unchanged; \`insta service list\` shows its protocol)` | |
| if (endpoint === 'http') return r.url || `${r.endpointHost}:${port ?? '?'}` |
| if (s.endpoint === 'tcp') return tcpAddress(s) | ||
| const protos = s.endpoint === 'tcp+udp' ? ['tcp', 'udp'] : ['udp'] | ||
| if (!s.endpoint_host) return `${s.endpoint === 'tcp+udp' ? 'tcp+udp' : 'udp'}/${s.port ?? '?'} (address after the first deploy)` | ||
| return protos.map((pr) => `${pr}://${s.endpoint_host}:${s.port}`).join(', ') |
There was a problem hiding this comment.
P3: The UDP/tcp+udp address can print :undefined or :null when the service response has a host but no port. Use the same s.port ?? '?' fallback as the no-host branch.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/commands/services.ts, line 148:
<comment>The UDP/tcp+udp address can print `:undefined` or `:null` when the service response has a host but no port. Use the same `s.port ?? '?'` fallback as the no-host branch.</comment>
<file context>
@@ -116,21 +116,38 @@ function defaultDatabasePort(type: string): number {
+ if (s.endpoint === 'tcp') return tcpAddress(s)
+ const protos = s.endpoint === 'tcp+udp' ? ['tcp', 'udp'] : ['udp']
+ if (!s.endpoint_host) return `${s.endpoint === 'tcp+udp' ? 'tcp+udp' : 'udp'}/${s.port ?? '?'} (address after the first deploy)`
+ return protos.map((pr) => `${pr}://${s.endpoint_host}:${s.port}`).join(', ')
+}
+
</file context>
| return protos.map((pr) => `${pr}://${s.endpoint_host}:${s.port}`).join(', ') | |
| return protos.map((pr) => `${pr}://${s.endpoint_host}:${s.port ?? '?'}`).join(', ') |
…endpoint only as fallback instacloud-platform#681 now returns `endpoint` beside `endpointHost` on POST /deploy and on a live archive-deploy read, including a redeploy that passed none. deployedTarget prints the scheme from it; the flag is the fallback for an older platform, and the schemeless line only appears when the result has a host but no endpoint. A plain redeploy of a tcp service prints exactly `tcp://host:port` again. The archive outcome and its --json carry the endpoint; an unknown value is treated as absent. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Review of Objectives and scope Reviewed the six changed files against the PR contract, repository AGENTS.md/development guidance, and companion platform #681's request/result contract. Traced local validation, service creation/listing, image and archive deployment, endpoint precedence, JSON output, and compatibility fallbacks. Verified the local source copy against every tracked file's Git blob hash. Critical None detected within the review scope. Suggestions
Information The implementation keeps port policy on the platform, validates endpoint choices before deploy-side configuration/network work, and propagates normalized values through both deploy lanes. Result endpoint metadata takes precedence over the flag, while unknown/missing metadata uses the documented compatibility fallback. Existing HTTP/TCP rendering and JSON behavior have focused coverage. No additional reportable correctness, authorization, injection, credential-handling, dependency-change, or performance defect was detected in the inspected paths. Release still depends on deploying platform #681, as the PR states; this review does not validate that upstream deployment. Tests actually performed
Verdict: approved — no Critical findings; the documentation suggestions are non-blocking. |
Conflict: serviceListLine. Keeps the v6-lane address for tcp/udp/tcp+udp and adds main's 'public off' and private address suffixes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Updated review of Objectives and scope Rechecked the UDP/tcp+udp contract, validation, request propagation through both deploy lanes, endpoint-result precedence, JSON output, and HTTP/TCP compatibility. Compared against the previously reviewed head: the additional changes merge main's private-network commands and service-list fields; the UDP implementation is unchanged. Checked those interactions against repository guidance and verified every tracked local file against the current head's Git blob hash. Critical None detected within the review scope. Suggestions The previous two non-blocking documentation suggestions remain:
Information Endpoint rendering preserves main's new Tests actually performed
Limitations Tests used reused installed tools and genuine fflate 0.8.3 compiled from its upstream tag, rather than a clean lockfile installation. Full-suite, help-process, Windows/native-binary and live-platform checks remain unverified. Production source was not modified. Verdict: approved — no Critical findings; the documentation suggestions remain non-blocking. |
The CLI half of InsForge/instacloud-platform#681, which widens compute
endpointfromhttp | tcptohttp | tcp | udp | tcp+udp. The compute plane has served public UDP on its v6 lane in every region since 2026-10-14. This PR follows the shape of #368 (--endpoint tcp).Changes
service add/deploy:--endpoint <http|tcp|udp|tcp+udp>.udp+tcp), or--endpointon a non-computeservice addfails before any network call.--always-on.udp/tcp+udp. service add / deploy: --endpoint http|tcp, and show a tcp service's tcp:// address #368's check that accepts an emptyurlwhenendpointHostis present already covers them. The live read'sendpointis carried through (see below).service listand theservice addline renderudp://host:port, and for tcp+udptcp://host:port, udp://host:port. Before a deploy has confirmed the host they renderudp/<port>ortcp+udp/<port> (address after the first deploy).deploytakes the scheme from the result'sendpoint. #681 returns it besideendpointHostonPOST /deployand on a live archive-deploy read, including a redeploy that passed no endpoint, where it reports the endpoint the service kept.--endpointis used only if the result has a host but noendpoint(an older platform). If neither is available,deployprintshost:port (endpoint unchanged; \insta service list` shows its protocol)rather than guessingtcp://. An endpoint value this CLI does not know is treated as absent. **A plain redeploy of a tcp service prints exactlytcp://host:port`, as before.**--json: the image lane passes the result through, now includingendpoint. The archive lane addsendpointbesideendpointHost.service list --jsonpassesendpoint/endpoint_hostthrough.Decisions
endpointdecides the scheme, and the flag is only a fallback. The result reports the endpoint the service actually ended up on. Without it, a plain redeploy of a udp service would printtcp://. The scheme-less line appears only for an older platform that returns a host with noendpointand no flag. One service add / deploy: --endpoint http|tcp, and show a tcp service's tcp:// address #368 test row (reports a tcp deploy) now passes'tcp'explicitly, because it models a result that carries noendpoint.tcp://h:p, udp://h:p: one scheme per protocol, so each part is a valid URI on its own.tcp+udp://is not a real scheme.tcp+udpis accepted (case and whitespace normalized like the other values).udp+tcpis refused because the platform spells the pair one way only.udpmoving from the refused list to the accepted one, and the tcp deploy-target row above. These follow from the contract change; nothing was loosened.Promise → test (
test/endpoint-udp.test.ts, one row per outcome)udp,tcp+udp,UDP,TCP+UDPparseparseEndpoint: udp and tcp+udp›%j is %sudp+tcp,tcp+,tcp,udp,tcp udp,quicrefused, with the message… › %j is refusedservice addsends udp / tcp+udpsends endpoint %sdoes not mirror the platform port list%s fails locallythe added line …testsservice list: udp and tcp+udp rowstablev6Addressfor tcp is exactlytcpAddressv6Address for tcp is tcpAddress…service list --jsonpasses endpoint fields through--json passes endpoint and endpoint_host through untouchedrides the deploy body as %sendpointtcp / udp / tcp+udp with no flag (tcp is the exacttcp://line)deployedTarget reads the endpoint from the result›result … no flag… the result winsendpointfalls back to the flag, or to no scheme; an unknown result value counts as absenthost without endpoint …/… does not know …--portprints:?reports %stable--endpoint httphttp: the URL, unchangedrowssends endpoint %s and accepts an empty URL…endpoint, an older platform omits it, an unknown value is droppedcarries the read's endpoint/an older platform omits it/an unknown value is droppeddeploy()dies onudp+tcpbeforeApiClient.load/requireProjectan invalid pair (udp+tcp) dies before…deploy()archive lane: normalized udp in the body,udp://line,--jsonendpointHostarchive lane …testsdeploy()image lane: tcp+udp in the body, both-scheme line,--jsonpass-throughimage lane …testsdeploy()image lane, plain tcp redeploy: the exactdeployed img:2 -> tcp://…line, with no endpoint in the bodyimage lane, plain redeploy of a tcp service …deploy()archive lane, plain udp redeploy:udp://from the read, and--jsoncarriesendpointarchive lane, plain redeploy of a udp service …Gates
npm run typecheck: clean.npm test: 105 files, 2225 tests passed.Negative controls
I broke each piece alone and ran the full suite. Each run failed where expected, then the file was restored with
git checkout, andgit diff --quietconfirmed the restore was exact.parseEndpointback tohttp/tcponlyv6Addressrenders tcp+udp as udp onlydeployedTargetalways printstcp://deployedTargetguessestcp://without--endpointdeploy()drops the endpoint beforedeployedTargethttp/tcpservicesAddRequestBodydrops udp / tcp+udpdeployRequestBodydrops udp / tcp+udpdeployedTargetignores the result'sendpointdeploy()tests)deployedTargettrusts an unknown result endpointendpointendpoint--jsondropsendpointOrder and companions
endpointfield on the deploy result and the archive read. Before #681, the platform's schema should refuseendpoint: udp; I took that from #681's enum and did not test it. Against a platform without the resultendpointfield, the CLI falls back to the flag or to the scheme-less line, so it never prints a wrong scheme.cli-reference.mdin InsForge/instacloud-skills (AGENTS rule 4) will follow in its own PR, as service add / deploy: --endpoint http|tcp, and show a tcp service's tcp:// address #368's did.endpointargument should widen in InsForge/instacloud-mcp.🤖 Generated with Claude Code