Open your app securely on any device
You built an app in a workbench, and now you want to actually open it: in the
browser on your own machine, and on your phone. The app runs inside a container, so you can’t reach it
directly. Each way takes one command: clean URLs on your machine, and real HTTPS
you can open on any device, your phone included. That HTTPS is what a login or
an installable app needs. This guide follows one workbench, called acme, from an
empty project to an app you reach both ways.
The example app is a small progressive web app, because it makes the reason for the HTTPS step obvious: without it, a PWA won’t install. A PWA is an ordinary website that can install to the home screen and run offline, powered by a service worker the browser runs in the background. That service worker is the reason the last step of this guide exists. But nothing here is PWA-only: the same URLs and the same HTTPS are what you want for an app you just click through, or one you log into, on any device.
On your own machine
Section titled “On your own machine”This part happens entirely on your own computer. You build the app and get it reachable in your own browser.
Start the workbench
Section titled “Start the workbench”The app is a Java API with a web frontend, and you want Claude Code in the workbench to build it. Postgres to store things. So that is what you start with:
monoceros init acme --with-languages=node,java --with-features=claude --with-services=postgresNotice there is no port here. That is on purpose. You do not yet know which ports the app will actually serve on, and you add them later, once the app exists and tells you. Build the workbench:
monoceros apply acmeLet the agent build it
Section titled “Let the agent build it”Hand Claude the task and let it work (see Add components to a
workbench for the ways to reach the
agent). It scaffolds the app into a directory under projects/, shop here,
with the API and the web frontend inside. That directory name is the app’s name:
it is what you pass to commands like monoceros start and monoceros share
later. Because both servers are long-running and have to survive the session
that started them, the agent writes their start commands into the app’s own
launch config, at projects/shop/.monoceros/launch.json:
{ "targets": [ { "name": "api", "command": "./mvnw spring-boot:run", "port": 8080, "default": true }, { "name": "web", "command": "npm run dev", "port": 5173, "default": true } ]}Each target names the port its server listens on. That whole mechanism, and how to hand-edit the file, is in Long-running app servers.
But the app is still not reachable from your machine. A workbench is a sealed container, closed off from the host, so a server running inside it stays hidden until its port is opened to the outside. Opening a port is a change on the host, which the agent cannot make from inside the container. So it stops and asks you to open the ports. That is the next step.
Expose the ports
Section titled “Expose the ports”The workbench needs to know which two ports should be reachable from outside the
container. That is what add-port is for:
monoceros add-port acme 5173 8080This takes effect at once. The proxy picks up the routes on the spot, with no
apply and no rebuild, so the ports are reachable the moment the command
returns. The change is also written to the yml:
routing: ports: - 5173 - 8080So the routes are part of the workbench’s reproducible config, not a one-off:
they come back on the next monoceros apply acme, on this machine or anyone
else’s.
Each port gets its own hostname, <workbench_name>-<port>.localhost. In this
example that is acme-<port>.localhost, and the first port you add also answers
on the bare acme.localhost as the default. That is why 5173
goes first: the web frontend is what you open in a browser, so it gets the short
acme.localhost, while the API keeps acme-8080.localhost. These names need no
hosts-file editing: any *.localhost name resolves to 127.0.0.1 on its own
(RFC 6761), and a single
machine-wide proxy maps each name to the right container port. Because the
hostname carries the workbench name, two different workbenches can each run a
server on 5173 at the same time without fighting over the host, one at
acme-5173.localhost and the other at its own name. The full
picture is in The Monoceros proxy and
tunnels.
Start the servers
Section titled “Start the servers”The launch config is in place, so the app can be brought up. The agent already started it from inside the container, but you drive it from the host. Start the whole app:
monoceros start acme shopThat is monoceros start <workbench_name> <app>: the workbench name first, then
the app, which is the projects/ directory from the last step. It brings up the
default set in order, waiting for each server’s port to listen before starting
the next, so the API is up before the frontend that calls it. To start or stop
just one part, add --target with its name:
monoceros start acme shop --target apiAnd to bring it down again, the same shape with stop:
monoceros stop acme shopstop also takes --target to stop a single server instead of the whole app.
There is more on the runner, readiness, and how servers come back after a
rebuild in Long-running app
servers.
Open it in the browser
Section titled “Open it in the browser”The servers are up and the routes are live, so list what you can open:
monoceros port acme 5173 → http://acme.localhost (default) 5173 → http://acme-5173.localhost 8080 → http://acme-8080.localhostOpen http://acme.localhost and the app loads. Everything works, the service
worker included, so you can even install the PWA right here. A .localhost
address counts as a trusted page, which is the quiet reason all of it is fine on
your machine. It looks finished.
On your phone and other devices
Section titled “On your phone and other devices”Now take the same app off your computer and onto your phone, or any other device on the same network. Doing that turns out to need HTTPS, for a reason that only shows up once you leave your machine.
.localhost,
other devices through share over HTTPS.
Why the phone is different
Section titled “Why the phone is different”Now you open the same app on your phone, which is on the same wifi network. This
time acme.localhost does not help, because a .localhost name always refers to
the machine you are using: on the phone, it points at the phone, not at your
computer. The phone therefore has to reach your computer by its network address
instead, and it does so over plain HTTP. That is where things break, and the
cause is not in your code:
- The service worker refuses to register, so the PWA will not install and offline is gone.
- A login does not complete. Keycloak, and other identity providers, run a bit of cryptography in the browser to keep the sign-in safe, and the browser only allows that on a trusted page. Over plain HTTP on a network address, the page is not trusted, so the login fails before it really starts.
The common cause is one rule: the browser grants these things only to a secure
context, which in practice means HTTPS. Your .localhost address gets that for
free; a phone on your network does not. This is not really about PWAs. Anything
that needs a secure context runs into the same wall the moment you leave your own
machine.
Share it over HTTPS
Section titled “Share it over HTTPS”That is what share is for. It serves the app over HTTPS on your local network,
so the phone gets a trusted page and both problems disappear:
monoceros share acme shopThe shape is the same as start: monoceros share <workbench_name> <app>, the
workbench name and then the app. It reads that app’s launch config, so every
target with a port comes along, and it prints an address per server plus the one
thing you do once:
Sharing acme/shop on the local network:
api https://192.168.1.42:8080 https://your-mac.local:8080
web https://192.168.1.42:5173 https://your-mac.local:5173
Trust the local CA once (first device) for warning-free HTTPS: ~/.monoceros/ca/rootCA.pem
Press Ctrl+C to stop sharing.The padlock needs a certificate behind it, so Monoceros issues one from a small
certificate authority that lives only on your machine. A device believes that
certificate once you tell it to trust the authority, and that is the single
manual step: you trust rootCA.pem once on each device you want to open the
shared app from (a phone, a tablet, another computer, even this host), and from
then on shared apps just load there. Trusting the local
certificate has the exact steps
for every device, phones, tablets, and computers alike. With the CA trusted, the
login goes through and the PWA installs to the home screen.
See also
Section titled “See also”- Add components to a workbench - start a workbench and grow it a component at a time, the guide this one builds on.
- Long-running app servers - the launch
config,
monoceros-ctl, and how servers come back after a rebuild. - The Monoceros proxy and tunnels - how the
.localhosthostnames and the shared proxy work. - Trusting the local certificate
- the per-device trust steps that make
shareload warning-free.
- the per-device trust steps that make
monoceros add-port/monoceros port/monoceros share- the commands, with every flag.