Public endpoints and domains
This page explains the two ways a service can be reached — private inside your project, or published on the internet — what URL each gives you, and how TLS is handled. It also states plainly what does not exist yet: custom domains.
Private by default
A new service is internal: reachable only from inside the platform, not from the internet. Every service always has an internal URL of this shape:
http://<name>.<namespace>.svc.cluster.local
Other workloads in your project — agents, functions, other services — can call this address directly. It appears as status.internal_url on the service object and in the console's URL block.
"Internal" removes your service from the internet. It does not check who is calling. Any workload that can reach the platform's shared gateway can reach an internal service by addressing it by name. If your service needs to know its callers, add authentication in your own application code.
Publishing to the internet
Turn on publish and the platform gives your service a public HTTPS URL:
https://<name>-<project-short>.apps.codyhill.dev
The hostname is always computed from two parts: your service name and your project's short identifier, joined with a dash, under the shared apps.codyhill.dev domain. A service named checkout-api in project acme becomes https://checkout-api-acme.apps.codyhill.dev. You cannot choose a different hostname (see Custom domains below).
From the console: check Publish to the internet in the deploy dialog (or in Edit on an existing service). The service list shows a visibility badge — published or Internal — and warns when the live state and your requested state disagree while a change converges.
From the API: publish is a single boolean, settable at create time or later with a PATCH:
curl -sS -X PATCH "$CAI_SERVERLESS_API/v1/projects/$PROJECT/services/checkout-api" \
-H "Authorization: Bearer $CAI_TOKEN" -H 'Content-Type: application/json' \
-d '{"publish": {"enabled": true}}'
Publishing is asynchronous. Poll the service and watch for the external URL to appear:
curl -sS -H "Authorization: Bearer $CAI_TOKEN" \
"$CAI_SERVERLESS_API/v1/projects/$PROJECT/services/checkout-api" \
| jq '.status.phase, .status.published, .status.external_url'
You should see, once publishing completes:
"Ready"
true
"https://checkout-api-acme.apps.codyhill.dev"
Two honest details about that status:
external_urlis filled in only once the URL actually works. The platform does not report a public address until it has verified the hostname answers over valid TLS. Until then the field is empty.- The
Exposedcondition gatesReady. For a published service,status.phasewill not sayReadywhile the public URL is still being wired up. If publishing is stuck, theExposedcondition's message says where it is stuck — see troubleshooting.
To take a service off the internet again, PATCH {"publish": {"enabled": false}}. Deleting a service releases its public hostname.
TLS
TLS — the encryption behind https:// and the browser padlock — is handled entirely by the platform. All *.apps.codyhill.dev hostnames are covered by a shared wildcard certificate. There is nothing for you to provision, upload, or renew, and there is no option to bring your own certificate today.
Testing a revision directly with tags
When you split traffic between revisions, you can put a tag (a short label like canary) on a traffic target. A tagged revision gets its own addressable hostname, so you can send test requests straight to the new revision without touching the percentage split. The tag's URL appears in the url field of that target in status.traffic, and on the console's Revisions tab. See deploy a service for the traffic-split API.
Custom domains (not yet)
There is no customer-supplied custom domain support in the alpha. Your public URL is always https://<name>-<project-short>.apps.codyhill.dev — there is no CNAME setup, no domain-verification flow, and no way to serve on your own domain through the platform today.
One sharp edge worth naming: the API accepts and stores a publish.host field on the wire, but the platform ignores it — the hostname is always the computed one. Do not set it expecting a custom hostname.
If you need your own domain now, the honest workaround is to run your own proxy or CDN in front of the published URL, outside the platform.
Summary
| Setting | Value |
|---|---|
| Default visibility | Internal (publish.enabled: false) |
| Internal URL | http://<name>.<namespace>.svc.cluster.local — always present, plain HTTP, unauthenticated |
| Public URL | https://<name>-<project-short>.apps.codyhill.dev — appears in status.external_url only once live |
| Hostname choice | None — always computed from service name + project short ID; publish.host is ignored |
| TLS | Automatic, shared wildcard certificate; HTTPS on published services only |
| Custom domains | Not available in the alpha |
| Readiness | Exposed condition holds Ready until the public URL answers over valid TLS |
Next: troubleshooting covers what to do when publishing sticks, and the serverless API reference documents every field shown here.