monoceros share
Exposes an app and the workbench’s HTTP services to the local network - your
phone, a tablet, another laptop - over HTTPS, so any device on the same
Wi-Fi can open them. The proxy’s http://<name>.localhost URLs only resolve on
the host machine; share is how you reach things from elsewhere. It is a
foreground process: Ctrl+C stops sharing.
monoceros share <name> <app><name>- the workbench you created withmonoceros init.<app>- the app to expose: a folder underprojects/that has a.monoceros/launch.json. Every target in that file that declares aportis shared, on the same port on the host’s network interface (0.0.0.0). Without a launch configsharewarns and goes on with the services alone.--forward-ports <host:container>,...(optional) - publish busy ports under different host ports when a port is already taken. See When a port is already in use.
Services come along on their own, without being named: see Services are shared too.
Example
Section titled “Example”Your workbench is acme, the app lives in projects/web and delegates login to
a Keycloak service you added with
monoceros add-service acme keycloak. Start the app’s dev servers, then share
them:
monoceros start acme web # bring the app's dev servers upmonoceros share acme web # expose them to the LAN over HTTPSshare stays in the foreground and lists every address a device can use, per
target and per service. The app’s web target comes from its launch config;
Keycloak appears next to it because its catalog entry declares
httpPort: 8080, which is what marks a service as reachable from outside the
container:
Sharing acme/web on the local network:
web https://192.168.1.10:5173 https://your-host.local:5173
keycloak (service) https://192.168.1.10:8080 https://your-host.local:8080
Trust the local CA once (first device) for warning-free HTTPS: ~/.monoceros/ca/rootCA.pem
Press Ctrl+C to stop sharing.Both addresses are offered because devices differ: some resolve the .local
name, others need the IP. Open the web URL on your phone, on the same Wi-Fi. On
a device you have not used before, trust the certificate
once so the page loads without a
warning. Press Ctrl+C when you are done - nothing stays exposed in the
background.
HTTPS and the certificate
Section titled “HTTPS and the certificate”share serves HTTPS, not plain HTTP, because a bare LAN IP or .local name
over http is an insecure context in the browser - and there crypto.subtle
(so OIDC/PKCE logins) and service workers (so installable PWAs) are switched off.
HTTPS gives the device a real secure context, so a phone-first PWA actually works.
TLS is terminated by the share process using a certificate authority that
lives only on your machine (~/.monoceros/ca/rootCA.pem); its private key
never leaves the host. Each device that opens a share URL trusts that CA
once - see Trusting the local
certificate for the per-platform
steps (macOS, Windows, Linux, iOS, iPadOS, Android). Because it terminates HTTP,
share also forwards X-Forwarded-Proto/X-Forwarded-Host, so a backend behind
it (an OIDC issuer, say) builds correct https:// URLs.
What it shares
Section titled “What it shares”- All configured ports, from the launch config - not just the
defaulttargets, and not only the ones currently running. The launch config is the source of truth, so a target you start later (e.g. a Storybook on its own port) is reachable the moment it comes up. A forward to a not-yet-started target simply refuses connections until it is up. - Targets without a
portare skipped (there is nothing to expose). - The backend rides the frontend. If your frontend dev server proxies the
API under a relative path (Vite
server.proxy, Angularproxy.conf.json, CRAsetupProxy.js), the browser only ever talks to the frontend origin - so the one frontend port is enough.
Services are shared too
Section titled “Services are shared too”A phone that logs in needs to reach the login server, not only the app. So every
service in the workbench yml that carries an httpPort is shared alongside
the app, under its own name in the banner and on that port:
services: - name: keycloak image: quay.io/keycloak/keycloak:26.7 port: 8080 # in-container port, used by `monoceros tunnel` httpPort: 8080 # the port that may leave the containerThe catalog fills that line in for the services where it makes sense, and leaves
it out where it does not. Keycloak declares 8080,
Mailpit declares 8025 (the web inbox, not the 1025
your app sends mail to), RustFS declares 9001 (the
console, not the S3 API on 9000), Caddy declares 81. A
database has no httpPort and is never shared: share speaks HTTP, so a
Postgres behind it would answer nonsense. Reach those with
monoceros tunnel instead.
Both ways to change what goes out live in the yml and take effect on the next
share, with no apply in between: delete the httpPort line to keep a service
to itself, or set a different port to move it. If you picked that port yourself,
as you do for a reverse proxy, change the service’s own config to the same
number.
Make the dev server reachable
Section titled “Make the dev server reachable”A dev server has to be configured so the network (and the proxy) can reach it. The briefing tells your AI agent to do this, but if you wire it by hand:
- Listen on
0.0.0.0, not127.0.0.1. - Accept the host’s name - Vite
server.allowedHosts, Angular--allowed-hosts, CRADANGEROUSLY_DISABLE_HOST_CHECK. (Bare IPs are allowed by most dev servers without this; a.localname needs it.) - Do not pin the HMR / live-reload socket to a fixed host or port - let it
follow the page URL (for Vite, leave
server.hmrunset). Then HMR works over<name>.localhoston the host and over the LAN, same config.
These are dev-server-only settings; a production build ignores them.
The last one flips when a reverse proxy of your own sits
in front of the dev server. The browser then arrives on the proxy’s port, while
the dev server still advertises its own for the reload socket, and live reload
goes nowhere while the page itself works. There you do pin the client port (Vite:
server.ws.clientPort) to the port the browser used.
When a port is already in use
Section titled “When a port is already in use”share probes every port before it touches Docker, stops on a busy one, and says
who holds it.
A container published it. The message names it, image included. The usual
culprit is a share of the same workbench still running in another terminal, and
its name says so: the terminator is called
monoceros-share-<workbench>-<app>. Stop it there with Ctrl+C, or remove the
container by that name.
Cannot share acme/web: host port 8080 already in use.
Published by a running container: 8080 monoceros-share-acme-web (caddy:2.11.4)Nothing published it. Then it is your IDE: attached over Remote-SSH, VS Code,
Codium and JetBrains auto-forward the container’s ports to 127.0.0.1, which
docker cannot see and which cannot be reliably switched off. In the PORTS panel,
right-click the port and choose “Stop Forwarding Port”. It stays gone across
reconnects, and share then binds the original port unchanged.
Port 80 is a special case. That belongs to monoceros-proxy, the
machine-wide Traefik that serves every workbench with ports, and it stays. A
service that wants 80 gets a port of its own instead: change its httpPort in
the yml and its own config to the same number.
Whatever holds the port, you can also publish around it. Re-run with
--forward-ports, a comma-separated list of host:container pairs in Docker
-p order:
monoceros share acme web --forward-ports 15173:5173,18000:8000A service is named explicitly when a bare port would be ambiguous, which happens when an app target and a service use the same number:
monoceros share acme web --forward-ports 18080:keycloak:8080The busy ports are reachable on the host ports you chose
(https://your-host.local:15173); every port you do not list keeps its own
number. The bind stays on 0.0.0.0, so other devices reach it exactly as
before.
Behavior
Section titled “Behavior”- Foreground. The forwards live only while the command runs; Ctrl+C closes every one. Nothing stays exposed in the background.
- One app per host address. A device reaches the app by the host’s IP (or
.localname); that address routes to one workbench at a time. Share one app, not several at once. - HTTP apps only.
shareterminates TLS and reverse-proxies HTTP, so it is for browser-facing apps and services. For a database client or other non-HTTP tool, usemonoceros tunnel, which is a raw-TCP forward. - Nothing to share is an error. If the app declares no port and no service
declares an
httpPort,sharestops and names both places you could fill.
See also
Section titled “See also”- Trusting the local certificate - per-platform steps to trust the HTTPS certificate on each device.
monoceros tunnel- raw-TCP forward of a single service/port (DB clients, ad-hoc tools), loopback by default.- Long-running app servers - the launch
config that
sharereads, and the dev-server conventions. monoceros status- see which apps and targets are running.