- Run goku:
just runor:
go run ./cmd/goku- Open the apps:
- Public endpoint:
http://localhost:9000 - Admin panel:
http://localhost:9001
- (Optional) enable admin login:
GOKU_ADMIN_PASSWORD=my-secret just runWithout GOKU_ADMIN_PASSWORD, the admin UI is open (no logout button shown).
- Golink redirects backed by aliases with placeholder support (
{}and named placeholders) - Public and admin endpoints split by port (
:9000and:9001) - Live health JSON (
/healthz) on both ports and WebSocket stream (/ws/health) on public - Admin golink directory with:
- search
- sortable columns
- add / edit / delete
- enable / disable toggle
- clickable destination links
- clickable alias preview page with countdown redirect via public endpoint
- API key + optional password authentication for admin/API operations
- Config live-reload from disk
- Dashboard/API golink changes are written back to
config/config.yamlfor persistence
- Golink: a saved redirect entry.
- Alias: the short path or pattern you type, such as
ghorr/{subreddit}. - Destination: the target URL or URL template.
- Prometheus metrics (
/metrics) on admin
Edit config/config.yaml:
aliases:
- alias: 'gh'
destination: 'https://github.com'
- alias: 'gh/{owner}/{repo}'
destination: 'https://github.com/{owner}/{repo}'
- alias: 'r/{subreddit}'
destination: 'https://www.reddit.com/r/{subreddit}'
- alias: 'yt/{}'
destination: 'https://www.youtube.com/results?search_query={}'Placeholder rules:
- Single placeholder aliases can use
{}. - Multiple placeholders must be uniquely named.
- Destination placeholders must be defined by the alias pattern.
go/ prefix note:
- Keep using
go/style links if that is your workflow (for examplehttp://go/ghorhttp://go/r/golang). - This assumes your DNS (or local host mapping) resolves
go(or a hostname likego.home.arpa) to your goku endpoint. - Without DNS/host routing, use the explicit server URL instead (for example
http://localhost:9000/gh).
Public (:9000):
| Method | Path | Description |
|---|---|---|
GET |
/ |
Public status page |
GET |
/{path...} |
Golink redirect |
GET |
/preview |
Golink redirect preview |
GET |
/healthz |
Health JSON |
GET |
/ws/health |
Health WebSocket stream |
Admin (:9001):
| Method | Path | Description |
|---|---|---|
GET |
/ |
Admin panel |
GET |
/login |
Login page (when password auth is enabled) |
POST |
/login |
Create admin session |
POST |
/logout |
Clear admin session |
GET |
/metrics |
Prometheus metrics |
GET |
/api/aliases |
List aliases |
POST |
/api/aliases |
Create/update alias |
POST |
/api/aliases/edit |
Edit alias |
POST |
/api/aliases/toggle |
Enable/disable alias |
POST |
/api/aliases/delete |
Delete alias |
POST |
/api/import |
Batch import aliases |
| Variable | Default | Description |
|---|---|---|
GOKU_API_PORT |
9000 |
Public endpoint port |
GOKU_ADMIN_PORT |
9001 |
Admin endpoint port |
GOKU_WEB_PORT |
9001 |
Admin port fallback when GOKU_ADMIN_PORT is unset |
GOKU_CONFIG |
config/config.yaml |
Config file path |
GOKU_PUBLIC_BASE_URL |
(empty) | Absolute public base URL used for admin preview links |
GOKU_ADMIN_USERNAME |
admin |
Username for basic auth |
GOKU_ADMIN_PASSWORD |
(empty) | Enables login page + session auth for admin UI |
GOKU_API_KEY |
(generated/file) | Admin API bearer token |
If you want links like http://go/gh on your LAN, set up local name resolution and route that hostname to goku.
- Pick a local hostname
- Recommended:
go.home.arpaor another local domain you control. - You can use single-label
goif your LAN resolver supports it.
- Configure DNS (or hosts as a fallback)
- DNS: create an
A/AAAArecord for your chosen host pointing to the machine running goku or your reverse proxy. - Hosts fallback (per client machine):
# /etc/hosts (macOS/Linux)
192.168.1.50 go go.home.arpa
- Route traffic to goku public port (
:9000)
- If you run goku directly on the host, send HTTP traffic for
goto:9000. - If you use a reverse proxy, point that hostname to
http://127.0.0.1:9000. - If you use Traefik with Docker/service autodiscovery, make the golink redirect router target the public port (
9000). The admin/API port (9001) shows the directory and CRUD API, but it intentionally returns 404 for alias paths.
Minimal examples:
go.home.arpa {
reverse_proxy 127.0.0.1:9000
}server {
listen 80;
server_name go.home.arpa;
location / {
proxy_pass http://127.0.0.1:9000;
}
}# Traefik labels (example)
traefik.http.routers.goku.rule=Host(`go.home.arpa`)
traefik.http.services.goku.loadbalancer.server.port=9000- Validate
- Open
http://go.home.arpa/gh(orhttp://go/ghif using single-label hostnames). - Run
curl -I http://go.home.arpa/ghand confirm a redirect response.
- When
GOKU_ADMIN_PASSWORDis set:- Admin UI requires login.
- API can use session, basic auth, or bearer key.
- When
GOKU_ADMIN_PASSWORDis empty:- Admin UI is open.
- Bearer key can still protect API endpoints when configured.
The API key is generated on first run and stored at config/.api_key if not supplied via GOKU_API_KEY.
just fmt
just test
just buildGoku arguably started as an itch I first scratched during an internship at Cisco, where I built an auto-correcting URL shortener and redirection tool for the intranet there. The first of its kind at the time, it was a massive hit among my peers. So much so that the idea stuck with me.
There have since been multiple solutions and iterations in my homelab including janky DNS scripts (rite of passage), some Traefik magic and a one-off Caddy config, but none satisfied the itch especially when faced with expanding scenarios like multi-level queries (e.g. t/TWS-4291/comments), so I embarked on rewriting the concept from scratch and in Go.
The name goku (悟空) is eternally recognizable for anyone familiar with DBZ and also means enlightenment through emptiness. Match made in heaven for this project.
Made with ♥ in NYC
