Per docs/SETUP.md's documented build recipe. control-api/prober/
validator-agent are unaffected (only internal/dashboard changed) and keep
their existing hashes in bin/SHA256SUMS.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
"Перепроверить выбранные" joins the existing "Удалить выбранные" / "Очистить
всё" bulk actions, using the checked-row selection the same way delete
already does — no control-api changes needed, since forcing a recheck of a
batch of addresses (new, finished, or already-queued, skipping anything
mid-check) is exactly what POST /api/v1/admin/ips (db.SubmitIPs) already
does, and the dashboard's own SubmitIPs client method already backs both
the top add/recheck form and the single-row recheck button.
The page also now auto-refreshes every 5s (handleIPsFragment + GET
/ips/fragment), mirroring the overview page's existing hx-trigger="every
Ns" polling and reusing the same config-driven interval
(Cfg.OverviewPollIntervalS) rather than adding a duplicate knob. Since the
table now polls itself, each row's selection checkbox gets a stable id
plus hx-preserve so a checked box survives the refresh (its own DOM node
is kept) while the rest of the row still updates live.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
docker-compose.yml now runs from prebuilt images (civ-capi/civ-adash/
civ-prober) instead of building from source, mounts this host's own
rxprod-compose/control-api.yaml and capi-db/ (both local, gitignored
except control-api.yaml itself which is now committed), and moves
control-api off port 8080 onto 8081.
rxprod-compose/sources/ carries local copies of the example configs
(admin-dashboard/control-api/prober/validator-agent) plus .env.example,
moved here from rxprod-compose/.env.example, for reference alongside
this specific deployment's docker-compose.yml.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
control-api.yaml is the actual bootstrap config the rxprod-compose stack's
control-api container mounts (per its docker-compose.yml), previously
undocumented/uncommitted — credentials stay out of it entirely (only the
env var names to read them from, per its own header comment).
.gitignore now also excludes rxprod-compose/capi-db/ (the stack's live
SQLite database — runtime state, not source) and graphify-out/ (this
session's /graphify knowledge-graph output — regenerable, not source).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Each target group's relationship to the check types that reference it was
previously invisible without cross-referencing /check-types by hand.
handleTargetsPage/renderTargetsTable now resolve that server-side via
loadTargetsPage (group -> referencing check types, enabled or not) and
render it as a status pill per group, plus a stats row (group count,
total targets, check types using them, unused groups) kept live via an
out-of-band swap on every create/update/delete.
Also auto-sizes the targets textarea as you type (targets.html), and
lets the stat-card grid collapse to however many state cards actually
exist instead of leaving empty background where a fixed repeat(6, ...)
had no card to fill.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Both binaries registered with control-api exactly once at startup and
exited (os.Exit(1)) on any failure — including control-api simply not
being up yet (no ordering guarantee between the two at boot/redeploy) or
the admin not having added this validator_id/site_id to the config yet.
Run() now retries registration with capped exponential backoff (3s->30s)
until it succeeds or the process is asked to shut down, instead of
crashing; registerWithRetry is identical in agentcore and probercore
since their Run/register shape already was.
Separately, the admin dashboard's Validators page had no hostname column
even though the agent already reports one on register (mirroring the
prober) and control-api already persists it — only the admin-config read
DTO (validatorDTO in httpapi and dashboard) dropped it before it reached
the template. Added hostname + last_heartbeat_at to that DTO end-to-end
and a Хост/Heartbeat column to validators.html, matching sites.html.
Rebuilt bin/{control-api,admin-dashboard,prober,validator-agent} and
bin/SHA256SUMS per docs/SETUP.md's documented build recipe.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The existing deploy/docker/docker-compose.yml (+ override/prod, Compose
profiles) is flexible but requires understanding profiles and file
layering. For a server that only ever runs these three fixed roles
against a real OpenStack (VK Cloud) deployment, rxprod-compose/ adds a
single flat docker-compose.yml with no profiles — control-api,
admin-dashboard and prober wired together directly, both ports published
on the host (control-api needs to be reachable by validator-agent running
separately on real cloud VMs). OpenStack credentials and the prober's
site_id come from a local .env (gitignored; .env.example is the
template).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NeVbMVEiE7XQAkBd7HQgj6
The cloud is live: the address list submitted as "free" (bootstrap config
or POST /api/v1/admin/ips) can drift by the time the orchestrator claims
it, or an operator can queue an already-occupied address by mistake.
Neutron's floating-IP association is a blind "last write wins" PUT with
no conflict error to catch, so associateFIP now checks the FIP's PortID
(already fetched via GetFloatingIPByAddress) before associating, guarded
against the false-positive of the FIP already belonging to this same
validator's own port.
A match routes the address straight to a new terminal ip_queue.state
("occupied", distinct from failed/fail) via db.MarkFIPOccupied — no
retries, since Neutron won't free it on its own and requeuing would let
it be reclaimed again next tick, starving the rest of the queue — plus a
dedicated fip_occupied audit event. Resubmitting the address later (once
the conflict is resolved) resets it to queued via the existing
POST /api/v1/admin/ips resubmit path (CancelIP/ListExpiredLeases updated
to treat occupied as terminal too). admin-dashboard gets its own "занят"
badge, distinct from fail/partial/cancelled.
Rebuilt bin/{control-api,admin-dashboard,prober,validator-agent} and
bin/SHA256SUMS per docs/SETUP.md's documented build recipe, since
control-api and admin-dashboard source changed.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NeVbMVEiE7XQAkBd7HQgj6
Standalone HTML page (docs/CONTROL_DATA_PLANE.html) showing the
docker-compose deployment topology (hosts, COMPOSE_PROFILES) and the
egress/inbound check traffic, refined through several presentation
review passes. Linked from README.md's docs table and docs/DIAGRAMS.md
alongside the existing Mermaid diagrams.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NeVbMVEiE7XQAkBd7HQgj6
control-api was the only component deployable via a bare Dockerfile alone
(no entrypoint/template, no default mountable config), and there was no
compose file wiring the four services' network, healthcheck ordering, or
DB volume together. Adds a base docker-compose.yml plus dev (override,
auto-loaded) and prod overlays, split by Compose profiles matching the
real deployment topology (control-plane/dashboard/prober/validator), a
ready-to-copy mock config for control-api so `docker compose up` works
out of the box, and the repo's first .gitignore for the local env/config
copies developers create from the committed examples.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NeVbMVEiE7XQAkBd7HQgj6