Document cover
Short Posts
A curated collection of bite-sized posts on state machines (agent visualization, UI modeling, team adoption), AI & agent workflows, alongside other short-form explorations in CSS, product design, leadership, and internet curiosities.

This section of my site is focused on small pieces of content that I want to share with you all about either ideas I have to things I see on the internet.

enjoy!

State Machines


See the State Machines — Knowledge Map for the full cluster across accounts.

AI & Agent Workflows


Web & Hypermedia


All Posts

<!-- embed: hm://z6Mkvz9TgGtv9zsGsdrksfNk1ajbFancgHREJEz3Y2HsAVdk/short-posts/jean-web-access-behind-tailscale; title: Jean Web Access behind Tailscale -->
If Jean Web Access works in a browser but the native Jean app, phone, or another tailnet device cannot connect, check whether Jean is being reached directly over HTTP or through Tailscale Serve. <!-- id:Za7Acojm -->

Recommended setup

Use Tailscale Serve as the tailnet entrypoint and bind jean-server to localhost:

# /etc/jean-server.env JEAN_HOST=127.0.0.1 JEAN_PORT=3456 JEAN_TOKEN=<long-random-token> JEAN_ALLOWED_ORIGINS=*

Restart Jean and publish it through Tailscale Serve:

sudo systemctl restart jean-server.service sudo tailscale serve --https=443 off sudo tailscale serve --bg http://127.0.0.1:3456 tailscale serve status

Use the HTTPS Tailscale Serve URL everywhere, including phones and the native Jean desktop app:

https://<machine-name>.<tailnet-name>.ts.net/?token=<JEAN_TOKEN>

<machine-name> is the device name in Tailscale. You can choose any name you want in Tailscale (for example, jean-server, build-box, or home-lab) and use that name in the URL.

For example, if you named the device jean-server in Tailscale:

https://jean-server.tail71152.ts.net/?token=<JEAN_TOKEN>

Why the direct Tailscale IP may stop working

When JEAN_HOST=127.0.0.1, Jean only listens on the server itself. That is intentional for the recommended Tailscale Serve setup. Other tailnet devices cannot connect directly to:

http://100.x.y.z:3456

They must use the Tailscale Serve HTTPS URL instead. Tailscale Serve receives the tailnet HTTPS request and proxies it locally to http://127.0.0.1:3456 on the server.

Quick checks

From any tailnet device, these should return 200 OK:

curl -i https://<machine-name>.<tailnet-name>.ts.net/readyz curl -i 'https://<machine-name>.<tailnet-name>.ts.net/api/auth?token=<JEAN_TOKEN>'

Expected /readyz body:

{"http":true,"ok":true,"websocket_broadcaster":true}

If the HTTPS URL returns a proxy error, confirm jean-server is listening on 127.0.0.1:3456 and restart Tailscale Serve.

Is this a Jean issue?

This is mostly a deployment/configuration gotcha for Jean servers behind a tailnet, not a core Jean server bug. The confusing part is that there are two valid modes:


    Tailscale Serve mode (recommended): JEAN_HOST=127.0.0.1, connect with https://...ts.net/?token=....

    Direct tailnet HTTP mode: JEAN_HOST=100.x.y.z or 0.0.0.0, connect with http://100.x.y.z:3456/?token=....

The modes are easy to mix up. If Jean binds to localhost, direct Tailscale IP access will fail. If Jean binds to the Tailscale IP, Tailscale Serve must proxy to that IP instead of 127.0.0.1, or it will fail.
<!-- /embed: hm://z6Mkvz9TgGtv9zsGsdrksfNk1ajbFancgHREJEz3Y2HsAVdk/short-posts/jean-web-access-behind-tailscale -->

Learning from seeing Devin prompting itself

Recently I asked Devin to create anot her session to implement Stripe payments on my side project, and I found very interesting the prompt structure it used. Here’s the “template” Implement <feature to implement> in <repo (app)>, covering X flows: A) flow 1… B) flow 2… == Repo context == List of both technical and code architecture concerns == Task A: task definition == Recommended architecture: … Required behavior: 1. behavior 1 2. Behavior 2 3. … == Task B: task definition == Recommended architecture: … Required behavior: 1. behavior 1 2. Behavior 2 3. … == Engineering requirements == * Technical and workflow requirements * … * If something genuinely blocks you that only the owner can answer (e.g. …), do not fake it — note it in the PR and message it back clearly. I really like this structure, because even though there are technical parts, it focused more on behavior and user intent rather than explicit technical concerns and concrete “steps” This is the way I usually prompt too, because I stop caring much about actual implementation, and more into following the correct logic that will let users achieve their goals based on user tasks. What helps me achieve this is think UX and UI as state machines: events that trigger state transitions and side effects that happens on those transitions. That’s it. What do you think? What “techniques you use to achieve alignment with your agents and help your users achieve their goals?

Could not load embed

Do you like what you are reading? Subscribe to receive updates.

Unsubscribe anytime