This app does not depend on `certifi`. Python still uses the image trust store for outbound TLS, and `python:3.12-slim` does not yet install the patched `ca-certificates` package.
The image install now upgrades `ca-certificates` and regenerates the bundle so `emSign ECC Root CA - G3` and `Comodo AAA Services` are not trusted.
Merge this, then redeploy beta and prod.
Reviewed-on: #13
Closes#11.
## Summary
- Serve Django admin on LAN IP `10.0.0.128` (not `piha.li`). Compose now publishes `0.0.0.0:8005` and passes `SHORT_ADMIN_HOSTS` into the container.
- Admin index has a campaign mint form: domain, campaign, source, metric. Save builds `https://{domain}/?utm_campaign=&utm_source=&utm_medium=` (metric) and shows a copyable short URL.
- Public `/admin/` on `piha.lc` / `piha.li` stays 404.
## Test plan
- [ ] `cd site && uv run python manage.py test`
- [ ] Recreate compose (`docker compose up --build`) so `WEB_BIND` / `SHORT_ADMIN_HOSTS` take effect
- [ ] From another LAN machine: `http://10.0.0.128:8005/admin/` (staff login) shows the mint form
- [ ] Save a link for an allowlisted domain, copy the short URL, confirm it 302s
- [ ] `https://piha.li/admin` still 404
Reviewed-on: #12
## Summary
- Copy company_site square `favicon.png` (gear/infinity mark) into `site/core/static/core/img/`
- Point `<link rel="icon">` at it instead of the wide wordmark `logo.png`
Closes#9
## Test plan
- [ ] Load the landing page and confirm the browser tab uses the square company mark, not the full wordmark
- [ ] Confirm `logo.png` is unchanged (still available for in-page branding)
Reviewed-on: #10
## Summary
- Serve `/api/` on `piha.lc` / `beta.piha.li` (Bearer still required).
- Drop `shortener.aimloperations.com` / `shortener-beta.aimloperations.com` from env examples and caller docs.
- `/admin/` stays 404 on the public host.
Closes#7.
## Test plan
- [ ] `POST https://beta.piha.li/api/links/` with valid Bearer → 201
- [ ] Same without Bearer → 401
- [ ] `GET https://beta.piha.li/<code>` still 302
- [ ] `/admin/` on beta.piha.li → 404
- [ ] Unit tests: `cd site && uv run python manage.py test`
Reviewed-on: #8
## Summary
- Drop the company_site nav (logo, Home, Web Design, Contact) from the shortener chrome — that header belongs on aimloperations.com, not `piha.lc`
- Add a **Why Piha?** section: Quenya (Tolkien High Elvish) *pia* / *pitya* / *pincë* / *picina* means little / small; a short link is a little path
- Footer and Request access / Contact CTAs stay
Closes#5.
## Test plan
- [ ] `cd site && uv run python manage.py test`
- [ ] `GET /` has no top header; Why Piha? is on the page
- [ ] Request access / Contact still go to aimloperations.com/contact
- [ ] Debug mint page still works without the nav
- [ ] `GET /<code>` still 302
Reviewed-on: #6
## Summary
- Public short domain is **`piha.lc`** (prod) and **`beta.piha.li`** (beta)
- `/` is a marketing page in [aimloperations.com](https://aimloperations.com) / `company_site` styling (Inter, dark, cyan)
- CTA: want access → [contact](https://aimloperations.com/contact)
- Nav/logo/footer match the company site; debug mint form uses the same chrome
Closes#3.
## Secrets (control node)
Update hostnames only — no new token/pepper unless you are rotating:
**`url_shortening_service_prod.env`**
```text
DJANGO_ALLOWED_HOSTS=piha.lc,shortener.aimloperations.com,url-shortener,web
SHORT_DOMAIN=piha.lc
PUBLIC_SHORT_URL=https://piha.lc
SHORT_PUBLIC_HOSTS=piha.lc
CONTACT_URL=https://aimloperations.com/contact
```
**`url_shortening_service_beta.env`**
```text
DJANGO_ALLOWED_HOSTS=beta.piha.li,shortener-beta.aimloperations.com,url-shortener,web
SHORT_DOMAIN=beta.piha.li
PUBLIC_SHORT_URL=https://beta.piha.li
SHORT_PUBLIC_HOSTS=beta.piha.li
CONTACT_URL=https://aimloperations.com/contact
```
NPM: `piha.lc` / `beta.piha.li` → this container. Still do not proxy `/api/` on the short host.
## Test plan
- [ ] `cd site && uv run python manage.py test`
- [ ] `GET /` looks like aimloperations.com; Request access / Contact go to aimloperations.com/contact
- [ ] `GET /<code>` still 302
- [ ] `/api/` on Host `piha.lc` is 404
Reviewed-on: #4