fix: move newsfeed-api off 8081 to 22672 (conflict-unlikely port)
8081 is the alt-HTTP port — one of the crowded defaults every proxy/dev server grabs — so it's collision-prone on a shared host. Move to 22672, a registered- range port in the sparse 20000s band, derived deterministically from the service name and recorded in architecture port-allocations.md. Updated everywhere the number appears: api bind config + default, firewalld service XML, nginx upstream, vite dev proxy, deploy workflow API_PORT, infra-setup (SELinux semanage label), and docs. Operator note: re-run script/infra-setup.sh on slartibartfast to relabel the SELinux port (22672) and ship the updated firewalld XML before the next deploy restarts the api on the new port. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016fKZzDpvjiJ9eYbPGgJvUP
This commit is contained in:
@@ -30,7 +30,7 @@ concurrency:
|
||||
env:
|
||||
API_HOST: slartibartfast.kosherinata.internal
|
||||
WEB_HOST: oolon.kosherinata.internal
|
||||
API_PORT: "8081"
|
||||
API_PORT: "22672"
|
||||
SSH_OPTS: -o StrictHostKeyChecking=accept-new
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -57,7 +57,8 @@ Crypto/token logic lives in `core::auth`; the flow orchestration in `core::servi
|
||||
|
||||
api + worker → `slartibartfast.kosherinata.internal`; SPA → `oolon.kosherinata.internal`
|
||||
(nginx serves + proxies `/v1` to the API over the mesh; TLS terminates at oolon). API
|
||||
port 8081, plain HTTP behind firewalld. See `.gitea/workflows/deploy.yml` (infra truth)
|
||||
port 22672 (registered in architecture `port-allocations.md`; deliberately not the crowded
|
||||
`8081`), plain HTTP behind firewalld. See `.gitea/workflows/deploy.yml` (infra truth)
|
||||
and `script/infra-setup.sh` (one-time host prep). `Type=exec`, not `notify`, because axum
|
||||
doesn't `sd_notify`.
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
# Bind the mesh-routable address so the oolon proxy can reach it. firewalld
|
||||
# (newsfeed-api service) bounds who may connect; TLS terminates at oolon.
|
||||
bind = "0.0.0.0:8081"
|
||||
bind = "0.0.0.0:22672"
|
||||
|
||||
database_path = "/var/lib/newsfeed/newsfeed.db"
|
||||
session_ttl_days = 30
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
<short>newsfeed-api</short>
|
||||
<description>newsfeed REST/JSON API. Reached over the WireGuard mesh by the oolon edge
|
||||
proxy, which terminates TLS and reverse-proxies to this port.</description>
|
||||
<port protocol="tcp" port="8081"/>
|
||||
<port protocol="tcp" port="22672"/>
|
||||
</service>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# ln -sf ../sites-available/newsfeed.internal.conf /etc/nginx/sites-enabled/
|
||||
|
||||
upstream newsfeed_api {
|
||||
server slartibartfast.kosherinata.internal:8081;
|
||||
server slartibartfast.kosherinata.internal:22672;
|
||||
keepalive 16;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ pub struct Config {
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
bind: "127.0.0.1:8081".parse().expect("valid default bind addr"),
|
||||
bind: "127.0.0.1:22672".parse().expect("valid default bind addr"),
|
||||
database_path: PathBuf::from("/var/lib/newsfeed/newsfeed.db"),
|
||||
session_ttl_days: 30,
|
||||
max_db_connections: 5,
|
||||
@@ -42,7 +42,7 @@ impl Default for Config {
|
||||
|
||||
impl Config {
|
||||
/// Load configuration, layering an optional TOML file and the environment over the
|
||||
/// defaults. Env vars are `NEWSFEED_` prefixed (e.g. `NEWSFEED_BIND=0.0.0.0:8081`).
|
||||
/// defaults. Env vars are `NEWSFEED_` prefixed (e.g. `NEWSFEED_BIND=0.0.0.0:22672`).
|
||||
pub fn load(file: Option<&Path>) -> anyhow::Result<Self> {
|
||||
let mut fig = Figment::from(Serialized::defaults(Config::default()));
|
||||
if let Some(path) = file {
|
||||
|
||||
18
readme.md
18
readme.md
@@ -65,9 +65,9 @@ cd web && pnpm install && pnpm build
|
||||
## Run locally
|
||||
|
||||
```sh
|
||||
# 1. API (creates ./data/newsfeed.db, binds 127.0.0.1:8081)
|
||||
# 1. API (creates ./data/newsfeed.db, binds 127.0.0.1:22672)
|
||||
NEWSFEED_DATABASE_PATH=./data/newsfeed.db \
|
||||
NEWSFEED_BIND=127.0.0.1:8081 \
|
||||
NEWSFEED_BIND=127.0.0.1:22672 \
|
||||
NEWSFEED_COOKIE_SECURE=false \
|
||||
cargo run -p newsfeed-api -- --config /nonexistent
|
||||
|
||||
@@ -75,7 +75,7 @@ NEWSFEED_COOKIE_SECURE=false \
|
||||
NEWSFEED_DATABASE_PATH=./data/newsfeed.db \
|
||||
cargo run -p newsfeed-worker -- --config /nonexistent
|
||||
|
||||
# 3. Frontend dev server (proxies /v1 and /health to :8081)
|
||||
# 3. Frontend dev server (proxies /v1 and /health to :22672)
|
||||
cd web && pnpm dev
|
||||
```
|
||||
|
||||
@@ -86,19 +86,19 @@ non-existent `--config` path just uses defaults + env, which is convenient for d
|
||||
|
||||
```sh
|
||||
# register + login (cookie jar)
|
||||
curl -c cj -X POST localhost:8081/v1/auth/register -H content-type:application/json \
|
||||
curl -c cj -X POST localhost:22672/v1/auth/register -H content-type:application/json \
|
||||
-d '{"username":"me","email":"me@example.com","password":"hunter2hunter2"}'
|
||||
curl -c cj -X POST localhost:8081/v1/auth/login -H content-type:application/json \
|
||||
curl -c cj -X POST localhost:22672/v1/auth/login -H content-type:application/json \
|
||||
-d '{"identifier":"me","password":"hunter2hunter2"}'
|
||||
|
||||
# weight an interest, mint an ingest token
|
||||
curl -b cj -X PUT localhost:8081/v1/interests -H content-type:application/json -d '{"label":"rust","weight":0.9}'
|
||||
TOKEN=$(curl -b cj -X POST localhost:8081/v1/tokens -H content-type:application/json -d '{"name":"agent"}' | jq -r .secret)
|
||||
curl -b cj -X PUT localhost:22672/v1/interests -H content-type:application/json -d '{"label":"rust","weight":0.9}'
|
||||
TOKEN=$(curl -b cj -X POST localhost:22672/v1/tokens -H content-type:application/json -d '{"name":"agent"}' | jq -r .secret)
|
||||
|
||||
# an agent pushes a candidate; read the ranked feed
|
||||
curl -X POST localhost:8081/v1/ingest/candidates -H "authorization: Bearer $TOKEN" \
|
||||
curl -X POST localhost:22672/v1/ingest/candidates -H "authorization: Bearer $TOKEN" \
|
||||
-H content-type:application/json -d '{"external_id":"a1","title":"Rust 2.0 released"}'
|
||||
curl -b cj localhost:8081/v1/feed
|
||||
curl -b cj localhost:22672/v1/feed
|
||||
```
|
||||
|
||||
## API surface (`/v1`)
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
# * a scoped /etc/sudoers.d/newsfeed_gitea_ci drop-in (visudo-verified)
|
||||
# and per role:
|
||||
# * API host (slartibartfast): the `newsfeed` service account, /etc/newsfeed +
|
||||
# /var/lib/newsfeed, SELinux port label for 8081, firewalld service
|
||||
# /var/lib/newsfeed, SELinux port label for 22672, firewalld service
|
||||
# * WEB host (oolon): /var/www/newsfeed webroot, the internal `newsfeed.internal` TLS
|
||||
# cert (lair provisioner) + step@newsfeed renewal, and the nginx vhost
|
||||
#
|
||||
@@ -19,7 +19,7 @@ set -euo pipefail
|
||||
# --- infra truth (edit here if the topology changes) -------------------------------
|
||||
API_HOST="slartibartfast.kosherinata.internal" # newsfeed-api + newsfeed-worker
|
||||
WEB_HOST="oolon.kosherinata.internal" # nginx: SPA + API reverse proxy
|
||||
API_PORT="8081"
|
||||
API_PORT="22672"
|
||||
CERT_NAME="newsfeed" # dot-free; serves ${CERT_NAME}.internal
|
||||
PUBLIC_FQDN="rob.fyi" # WAN name (Let's Encrypt); "" to skip
|
||||
CERT_EMAIL="ops@rob.fyi" # certbot registration email
|
||||
|
||||
@@ -9,8 +9,8 @@ export default defineConfig({
|
||||
server: {
|
||||
port: 5173,
|
||||
proxy: {
|
||||
'/v1': 'http://127.0.0.1:8081',
|
||||
'/health': 'http://127.0.0.1:8081',
|
||||
'/v1': 'http://127.0.0.1:22672',
|
||||
'/health': 'http://127.0.0.1:22672',
|
||||
},
|
||||
},
|
||||
build: {
|
||||
|
||||
Reference in New Issue
Block a user