This repository contains the deployment and operating configuration for the
public tunneling service at fedify.com.es.
This repository is public. Assume that every tracked file, commit, pull request, CI log, and build artifact can be read by anyone.
Never add live credentials or private key material. This includes Cloudflare tokens, vault passwords, ACME account keys, SSH private keys, TLS private keys, decrypted vault content, and rendered configuration containing secret values. Keep operator-specific values such as account email addresses and allowlisted SSH public keys out of the repository unless the owner explicitly approves their publication. Use obvious placeholders in examples and tests.
The verified sish host public key and its fingerprint are intended to be public client trust anchors. Publishing them is required; publishing the corresponding private host key is forbidden.
Inspect staged changes before every commit. .gitignore reduces accidental
additions but is not a security control. If sensitive data enters Git history,
stop publishing, revoke or rotate the exposed value, and remove it from the
history before continuing. Deleting it in a later commit is not sufficient.
Before changing the repository or the server, read these files in order:
ARCHITECTURE.mddescribes the intended system and its security boundaries.PLAN.mdtracks the implementation sequence and acceptance criteria.
Keep both documents current when an implementation choice changes the design or the remaining work.
- Run sish as the tunnel server. Do not
introduce
tunnelto-server; its WebSocket control protocol is incompatible with the SSH-basedlocaltunnelclient. - Run sish directly under systemd. This deployment does not use Docker or another container runtime.
- Configure the host with Ansible over SSH. Playbooks must be idempotent.
- Use
miseas the local command and tool-version entry point. - Obtain
fedify.com.esand*.fedify.com.escertificates from Let's Encrypt with Certbot's Cloudflare DNS plugin. - Start with an authenticated private beta. Anonymous public access is a later cutover decision, not a bootstrap default.
- Require tunnel clients to verify the pinned sish host key. Never use
StrictHostKeyChecking=noforfedify.com.es.
Do not reverse one of these decisions without documenting the reason in
ARCHITECTURE.md and updating PLAN.md first.
- Host:
fedify.com.es - Bootstrap access:
ssh root@fedify.com.es - Operating system: Ubuntu 26.04 LTS
- Public IPv4 address at initial planning time:
217.154.0.35 - DNS provider: Cloudflare
- DNS records: apex and wildcard A records point to the VPS
- Initial DNS mode: DNS-only, not Cloudflare-proxied
Treat these as recorded facts, not permanent assumptions. Check the live host, DNS, and upstream release metadata before applying a change.
The intended listeners are:
| Port | Purpose |
|---|---|
| 22/tcp | Administrative OpenSSH access |
| 2222/tcp | Public sish SSH endpoint |
| 80/tcp | Public HTTP endpoint and HTTPS redirect |
| 443/tcp | Public HTTPS endpoint |
Do not expose sish TCP forwarding, SNI forwarding, an admin console, or an additional listener unless the architecture and threat model are updated. Because sish 2.23.0 cannot reject every raw TCP remote-forward request, contain such listeners on loopback during the authenticated beta and require protocol-level rejection before anonymous access.
- Use a Cloudflare API token restricted to DNS edits for the
fedify.com.eszone. Do not use a global API key. - Mark Ansible tasks that handle secrets with
no_log: trueand suppress diffs for secret-bearing templates. - Pin the sish version and verify the upstream checksum before installation.
Do not deploy an unpinned
latestartifact. - Keep the sish SSH host key and ACME state persistent across upgrades.
- Publish the sish host public key only after confirming its fingerprint
through authenticated administrative access and an independent channel.
Do not treat unauthenticated
ssh-keyscanoutput as a trust anchor. - Run sish as a dedicated unprivileged system user. Grant only
CAP_NET_BIND_SERVICEso it can bind ports 80 and 443. - Write logs to the systemd journal unless a documented operational need calls for another sink.
- Make file updates atomic where a partial write could break startup, especially for the sish binary, configuration, and TLS credentials. Switch a certificate and its private key through one atomic reference; do not replace the two active paths separately.
- Point
https-certificate-directoryat a directory containingfedify.com.es.crtandfedify.com.es.key. sish pairs*.crtfiles with same-basename.keyfiles; Certbot'sfullchain.pemandprivkey.pemare source files, not the deployed names. - Use Ansible handlers so sish restarts only when its binary, configuration, or certificates change.
- Preserve the existing administrative SSH path during bootstrap. Allow port 22 in the firewall before enabling it, and verify a second SSH session before tightening SSH access.
- Do not change Cloudflare records from DNS-only to proxied as a side effect of deployment work. That change affects SSH endpoint naming, TLS, request handling, and abuse controls.
- Prefer Ansible built-in modules over shell commands. When a command is
unavoidable, give it an explicit idempotence condition and a focused
changed_whenrule.
The implementation should expose these commands through mise:
mise run check: validate formatting, Ansible syntax, and lint rules.mise run deploy:check: show the proposed production change with Ansible check and diff mode.mise run deploy: apply the production playbook.mise run verify: run non-destructive checks against the deployed service.
Inspect mise.toml before invoking a task. Until a task exists, do not claim it
has been run.
Before a production apply:
- Inspect
git statusand the complete diff. - Run the repository checks.
- Run
mise run deploy:checkand review every proposed change. - Confirm that the current SSH session can survive the firewall and service changes.
- Apply with
mise run deploy. - Run
mise run verifyand a real end-to-end tunnel test.
An Ansible check-mode run is a preview, not proof that the apply will succeed. Certificate issuance and other external operations may need additional staging or read-only validation.
Use checks appropriate to the changed layer. A complete deployment should cover:
- Ansible syntax and lint checks.
systemd-analyze verifyfor the rendered unit.systemctl is-active sishand recent journal inspection.- Listener checks for ports 22, 2222, 80, and 443.
- Negative raw TCP forwarding checks, including a fixed port and
-R 0, that prove no additional non-loopback listener is created or externally reachable. - Certificate hostname, chain, and expiration checks.
certbot renew --dry-run --run-deploy-hooksafter renewal automation changes.- A real reverse tunnel from a separate machine, followed by an HTTPS request to the allocated subdomain.
- A wrong-host-key test that proves SSH exits before creating a remote forward.
- A comparison between the key presented on port 2222 and the published pin.
- Tunnel cleanup after the SSH client exits.
Do not call the deployment complete when only the process or ports are up. The public HTTPS request must reach the local test server through the SSH tunnel.
Infrastructure files and technical documentation should be written in English. Discussion with the repository owner may be in Korean. Keep commit messages in English.
The fedify.com.es service definition belongs in Fedify CLI. Fedify CLI will
pass it to @hongminhee/localtunnel's openTunnel() function at runtime after
this service passes its beta and soak checks. Localtunnel 0.5.0 provides strict
host-key verification through Service.knownHosts; the Fedify service must
populate that field because omitting it retains legacy non-strict behavior. Do
not add the service to localtunnel's built-in registry or vendor either package
in this repository.