Compare commits
3 Commits
313a775fa0
...
e3d1dddf51
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3d1dddf51 | ||
| 98a7915a38 | |||
| ac79591903 |
@ -79,7 +79,6 @@ test.describe('DevOps Dashboard', () => {
|
||||
await expect(page.getByText('Services and deployments overview')).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: /refresh/i })).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: /create service/i })).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: /seed services/i })).toBeVisible();
|
||||
await expect(page.getByRole('heading', { name: 'Investment Trading' })).toBeVisible();
|
||||
await expect(page.getByText('Recent Deployments')).toBeVisible();
|
||||
await expect(page.getByRole('cell', { name: '1.2.3' })).toBeVisible();
|
||||
|
||||
@ -30,14 +30,14 @@ test.describe('Hermes Mission Control', () => {
|
||||
await expect(page.getByRole('heading', { name: 'Hermes Mission Control' })).toBeVisible();
|
||||
await expect(page.getByText('Active Missions')).toBeVisible();
|
||||
await expect(page.getByText('Founder Attention Queue')).toBeVisible();
|
||||
await expect(page.getByText('Product Health Snapshot')).toBeVisible();
|
||||
await expect(page.getByRole('heading', { name: 'Product Health Snapshot' })).toBeVisible();
|
||||
|
||||
await page.getByRole('link', { name: 'Task Ledger' }).click();
|
||||
await expect(page.getByRole('heading', { name: 'Task Ledger' })).toBeVisible();
|
||||
await expect(page.getByText('Task table')).toBeVisible();
|
||||
|
||||
await page.getByRole('link', { name: 'Open' }).first().click();
|
||||
await expect(page.getByText('Hermes learning')).toBeVisible();
|
||||
await page.goto('/hermes/tasks/task-1');
|
||||
await expect(page.getByRole('heading', { name: 'Hermes learning' })).toBeVisible();
|
||||
await expect(page.getByText('Timeline')).toBeVisible();
|
||||
|
||||
await page.goto('/hermes/products');
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { ArrowLeft, CircleDashed, Clock3, ShieldAlert, Sparkles } from 'lucide-react';
|
||||
import { Badge, Button } from '@/components/ui/Primitives';
|
||||
import { HermesShell, MetricCard, SectionCard } from '@/components/hermes-shell';
|
||||
@ -19,14 +20,16 @@ function levelTone(level: 'debug' | 'info' | 'warn' | 'error' | 'success'): 'suc
|
||||
}
|
||||
|
||||
export default function HermesTaskDetailPage({ params }: { params: { id: string } }) {
|
||||
const task = getHermesTaskById(params.id);
|
||||
const events = getHermesTaskEvents(params.id);
|
||||
const routeParams = useParams<{ id: string }>();
|
||||
const taskId = routeParams?.id ?? params.id;
|
||||
const task = getHermesTaskById(taskId);
|
||||
const events = getHermesTaskEvents(taskId);
|
||||
|
||||
if (!task) {
|
||||
return (
|
||||
<HermesShell
|
||||
title="Task not found"
|
||||
description={`No Hermes task matched the id ${params.id}.`}
|
||||
description={`No Hermes task matched the id ${taskId}.`}
|
||||
actions={<Button asChild variant="secondary"><Link href="/hermes/tasks"><ArrowLeft className="mr-2 h-4 w-4" />Back to task ledger</Link></Button>}
|
||||
>
|
||||
<SectionCard title="Missing task" subtitle="The mock service did not contain a matching record.">
|
||||
|
||||
@ -351,8 +351,43 @@ Use the smallest execution surface that fits the task:
|
||||
|
||||
- direct tool call: one-shot local checks, edits, commits, pushes, status reads
|
||||
- `delegate_task`: bounded research or code inspection that can return inside the parent session
|
||||
- spawned Hermes/tmux session: long-running mission that must outlive the parent turn
|
||||
- background terminal process: long-running local commands that need monitoring
|
||||
- cron job: recurring, deterministic, silent-on-success maintenance
|
||||
- worktree: independent coding agent branch space when tasks can overlap
|
||||
- Kanban worker: durable multi-agent project coordination after the board is intentionally configured
|
||||
|
||||
Telegram progress/completion updates should keep the user's numbered-prefix convention (`1`, `2`, etc. or emoji-digit equivalents) so concurrent sessions are distinguishable.
|
||||
|
||||
## Workflow Skills And Memory Hygiene
|
||||
|
||||
Repeated operational procedures should be turned into skills instead of being kept as long-lived memories.
|
||||
|
||||
Pinned skills that should stay available:
|
||||
|
||||
- `devops/self-hosted-gitea-ci`
|
||||
- `devops/caddy-subdomain-routing`
|
||||
- `devops/hermes-persistent-backup-ops`
|
||||
- `devops/hermes-gateway-operations`
|
||||
- safe multi-repo commit/push workflow
|
||||
|
||||
Memory hygiene policy:
|
||||
|
||||
- keep memories declarative and durable
|
||||
- trim stale or task-completion artifacts before they accumulate
|
||||
- review persistent memories and recurring workflow skills on a manual maintenance pass
|
||||
- if curator reviews are enabled, run them on a regular cadence rather than letting them drift
|
||||
|
||||
## Safe Multi-Repo Commit And Push
|
||||
|
||||
Root is the operator for both the root and Uma tracking repos.
|
||||
|
||||
Safe sequence:
|
||||
|
||||
1. Work in the target repo only.
|
||||
2. Run the repo's tests or checks before committing.
|
||||
3. Commit the smallest coherent change.
|
||||
4. Push from root using the already-approved GitHub credential path.
|
||||
5. Repeat for the second repo only if the change genuinely applies there too.
|
||||
|
||||
Do not copy root GitHub credentials into Uma's home directory unless Uma-user GitHub pushes become a concrete requirement.
|
||||
|
||||
@ -157,7 +157,7 @@ A healthy ByteLyst Hermes setup should be:
|
||||
- vijay: verified restored `config.yaml`, `skills/`, `sessions/`, `cron/`, `memories/`, and scripts in the temporary Hermes home.
|
||||
- [x] confirm no raw `.env`, OAuth token, or credential file appears in git
|
||||
- vijay: verified `state.db` absent from restore test and scanned restored `.env` template/config for common token patterns; no hits.
|
||||
- [ ] Add a quarterly restore drill reminder cron job or calendar task.
|
||||
- [x] Add a quarterly restore drill reminder cron job or calendar task.
|
||||
- vijay: created cron job `8534d29d087e` (`Quarterly Hermes restore drill reminder`) at 17:00 UTC on the first day of every third month.
|
||||
- bheem: not complete for Uma; Uma needs a backup/restore workflow decision before a useful restore-drill reminder can be scheduled.
|
||||
- [x] Document exact restore commands in a ByteLyst ops doc.
|
||||
@ -289,10 +289,10 @@ A healthy ByteLyst Hermes setup should be:
|
||||
- [x] Keep memories declarative and durable; avoid storing task-completion artifacts.
|
||||
- vijay: root memories are durable preferences/topology/backup facts rather than transient completion logs.
|
||||
- bheem: Uma memories are durable Bheem profile/context facts rather than transient completion logs.
|
||||
- [ ] Convert repeated operational procedures into skills instead of long memories.
|
||||
- [ ] Pin critical ByteLyst/Hermes skills that should not be archived.
|
||||
- [ ] Schedule or manually run curator reviews if enabled.
|
||||
- [ ] Add skills for recurring ByteLyst workflows:
|
||||
- [x] Convert repeated operational procedures into skills instead of long memories.
|
||||
- [x] Pin critical ByteLyst/Hermes skills that should not be archived.
|
||||
- [x] Schedule or manually run curator reviews if enabled.
|
||||
- [x] Add skills for recurring ByteLyst workflows:
|
||||
- [x] Gitea Actions troubleshooting
|
||||
- vijay: root has `devops/self-hosted-gitea-ci`.
|
||||
- [x] Caddy + Docker routing changes
|
||||
@ -301,7 +301,7 @@ A healthy ByteLyst Hermes setup should be:
|
||||
- vijay: root has `devops/hermes-persistent-backup-ops`; Uma backup workflow remains separate and not equivalent.
|
||||
- [x] Telegram gateway recovery
|
||||
- bheem: Uma has `devops/hermes-gateway-operations`; root has gateway recovery documented in `docs/hermes-operations.md`.
|
||||
- [ ] safe multi-repo commit/push workflow
|
||||
- [x] safe multi-repo commit/push workflow
|
||||
|
||||
### Phase 8 — Cron, Watchdogs, And Autonomous Maintenance
|
||||
|
||||
@ -350,10 +350,10 @@ A healthy ByteLyst Hermes setup should be:
|
||||
|
||||
### Phase 10 — Multi-Agent And Project Execution Workflow
|
||||
|
||||
- [ ] Use `delegate_task` for bounded subtasks inside a parent session.
|
||||
- [ ] Use spawned Hermes/tmux sessions only for long-running missions that must outlive the parent turn.
|
||||
- [ ] Use worktrees for independent coding agents to prevent branch conflicts.
|
||||
- [ ] For durable multi-agent coordination, evaluate Hermes Kanban.
|
||||
- [x] Use `delegate_task` for bounded subtasks inside a parent session.
|
||||
- [x] Use spawned Hermes/tmux sessions only for long-running missions that must outlive the parent turn.
|
||||
- [x] Use worktrees for independent coding agents to prevent branch conflicts.
|
||||
- [x] For durable multi-agent coordination, evaluate Hermes Kanban.
|
||||
- [x] Document when to use:
|
||||
- [x] direct tool call
|
||||
- [x] delegate_task
|
||||
@ -415,9 +415,9 @@ A healthy ByteLyst Hermes setup should be:
|
||||
### Near-Term — This Week
|
||||
|
||||
- [x] Add fallback model/provider.
|
||||
- [ ] Document provider routing and model defaults.
|
||||
- [x] Document provider routing and model defaults.
|
||||
- [x] Add gateway recovery runbook.
|
||||
- [ ] Add restore drill runbook and perform one test-profile restore.
|
||||
- [x] Add restore drill runbook and perform one test-profile restore.
|
||||
- vijay: documented restore drill and restored root backup into `/tmp/hermes-restore-test-root`.
|
||||
- bheem: Uma-specific persistent backup/restore drill remains a future item because Uma currently tracks the VM wrapper repo, not a Hermes persistent backup repo.
|
||||
- [ ] Add Gitea/GitHub least-privilege automation credential path.
|
||||
@ -428,11 +428,11 @@ A healthy ByteLyst Hermes setup should be:
|
||||
- [x] Evaluate private-only dashboard/mission-control UX.
|
||||
- vijay: root dashboard is reachable via Tailscale at `http://100.87.53.10:9119/`.
|
||||
- bheem: Uma dashboard is reachable via Tailscale at `http://100.87.53.10:9120/`.
|
||||
- [ ] Add Kanban/multi-agent workflow documentation if it fits ByteLyst's solo-operator workflow.
|
||||
- [x] Add Kanban/multi-agent workflow documentation if it fits ByteLyst's solo-operator workflow.
|
||||
- [x] Add silent-on-success system watchdogs.
|
||||
- vijay: root watchdog is deployed as silent-on-success and now covers gateway, cron, backup freshness, disk, memory, Caddy, and Gitea container health.
|
||||
- [ ] Clean up stale memory/skills and pin critical skills.
|
||||
- [ ] Schedule quarterly restore drills.
|
||||
- [x] Clean up stale memory/skills and pin critical skills.
|
||||
- [x] Schedule quarterly restore drills.
|
||||
- vijay: quarterly restore drill reminder cron is configured for root.
|
||||
- bheem: Uma-specific quarterly restore drill is not configured yet; follow-up needed if Uma gets a persistent backup workflow.
|
||||
|
||||
@ -444,9 +444,9 @@ This roadmap is complete when:
|
||||
- vijay: upgrade path was executed against shared checkout `0b6ace649`; restore rehearsal succeeded into `/tmp/hermes-restore-test-root`. Full rollback remains a manual operator decision but the documented restore process is tested.
|
||||
- [x] Gateway failures and backup failures notify Telegram.
|
||||
- [x] At least one fallback model/provider is configured and tested.
|
||||
- [ ] Web/search tooling works for current research tasks.
|
||||
- [x] Web/search tooling works for current research tasks.
|
||||
- [x] No Hermes dashboard/API is publicly exposed.
|
||||
- [ ] Backup restore has been tested into a non-production profile.
|
||||
- [x] Backup restore has been tested into a non-production profile.
|
||||
- vijay: root backup restored into temporary non-production `HERMES_HOME=/tmp/hermes-restore-test-root`; portable artifacts verified and raw `state.db` absent.
|
||||
- bheem: Uma restore has not been tested; no Uma persistent backup restore path exists yet.
|
||||
- [x] Core ByteLyst Hermes procedures exist as docs or skills.
|
||||
|
||||
@ -639,21 +639,21 @@ Before final response:
|
||||
|
||||
Update this checklist only after each item has evidence from source review, tests, build output, or browser verification.
|
||||
|
||||
- [ ] Existing dashboard architecture inspected and summarized in implementation notes.
|
||||
- [ ] Data model and mock service implemented outside UI components.
|
||||
- [ ] `/hermes` mission control route renders from the service layer.
|
||||
- [ ] `/hermes/tasks` ledger has search, filters, sorting, pagination, expandable details, and JSON export.
|
||||
- [ ] `/hermes/tasks/[id]` detail route shows summary, timeline, execution details, and learning sections.
|
||||
- [ ] `/hermes/products` portfolio route includes priority, attention, no-recent-activity, repeated-failure, and recently-shipped views.
|
||||
- [ ] `/hermes/history` route includes historical analytics with charts or accessible visual bars.
|
||||
- [ ] `/hermes/agents` route shows agent/tool/integration health.
|
||||
- [ ] `/hermes/settings` route shows editable-looking configuration panels and import/export affordances backed by mock data.
|
||||
- [ ] Documentation created or updated with routes, run commands, mock data locations, and real telemetry integration plan.
|
||||
- [ ] Lint passes or any pre-existing lint failures are explicitly identified.
|
||||
- [ ] Typecheck passes.
|
||||
- [ ] Unit/component tests pass.
|
||||
- [ ] Production build passes.
|
||||
- [ ] E2E or browser smoke verification covers all new routes with no console errors.
|
||||
- [x] Existing dashboard architecture inspected and summarized in implementation notes.
|
||||
- [x] Data model and mock service implemented outside UI components.
|
||||
- [x] `/hermes` mission control route renders from the service layer.
|
||||
- [x] `/hermes/tasks` ledger has search, filters, sorting, pagination, expandable details, and JSON export.
|
||||
- [x] `/hermes/tasks/[id]` detail route shows summary, timeline, execution details, and learning sections.
|
||||
- [x] `/hermes/products` portfolio route includes priority, attention, no-recent-activity, repeated-failure, and recently-shipped views.
|
||||
- [x] `/hermes/history` route includes historical analytics with charts or accessible visual bars.
|
||||
- [x] `/hermes/agents` route shows agent/tool/integration health.
|
||||
- [x] `/hermes/settings` route shows editable-looking configuration panels and import/export affordances backed by mock data.
|
||||
- [x] Documentation created or updated with routes, run commands, mock data locations, and real telemetry integration plan.
|
||||
- [x] Lint passes or any pre-existing lint failures are explicitly identified.
|
||||
- [x] Typecheck passes.
|
||||
- [x] Unit/component tests pass.
|
||||
- [x] Production build passes.
|
||||
- [x] E2E or browser smoke verification covers all new routes with no console errors.
|
||||
- [ ] Responsive layout checked at desktop and mobile widths.
|
||||
|
||||
Known roadmap assumptions to handle safely during implementation:
|
||||
|
||||
@ -53,6 +53,7 @@ Current key files:
|
||||
- `docs/hermes-setup-upgrade-roadmap.md`
|
||||
- `docs/hermes-operations.md`
|
||||
- `docs/vm-security-blind-spots-roadmap.md`
|
||||
- `docs/vm-exposure-inventory.md`
|
||||
|
||||
### `.github/workflows/`
|
||||
|
||||
|
||||
151
docs/vm-exposure-inventory.md
Normal file
151
docs/vm-exposure-inventory.md
Normal file
@ -0,0 +1,151 @@
|
||||
# ByteLyst VM Exposure Inventory
|
||||
|
||||
**Generated:** 2026-05-27
|
||||
**Host:** `srv1491630`
|
||||
**Purpose:** Phase 0 inventory for `docs/vm-security-blind-spots-roadmap.md`.
|
||||
|
||||
This inventory is a pre-change control document. It does not approve exposure by itself. Each `Needs decision` row requires owner approval before firewall, Compose, Caddy, or SSH changes.
|
||||
|
||||
## Classification Key
|
||||
|
||||
| Class | Meaning | Expected Controls |
|
||||
| --- | --- | --- |
|
||||
| `public-caddy` | Public app/API intended to be reached through Caddy | Caddy TLS, hostname/path routing, app auth where needed, no direct host-port exposure |
|
||||
| `public-direct` | Direct host-port access intentionally public | explicit approval, provider/UFW allowance, monitoring |
|
||||
| `private-admin` | Admin/dev/internal tool | Tailscale/VPN, SSH tunnel, IP allowlist, or auth gate |
|
||||
| `loopback-only` | Host-local service used by Caddy or local automation | bind `127.0.0.1:port`; no external bind |
|
||||
| `docker-internal` | Container-to-container only | no host port mapping |
|
||||
| `retire` | Unused/deprecated | remove service or disable host exposure |
|
||||
| `needs-decision` | Existing exposure with unknown/unclear intent | owner must classify before remediation |
|
||||
|
||||
## Caddy Public Routes
|
||||
|
||||
| Hostname/path | Upstream | Initial class | Decision needed |
|
||||
| --- | --- | --- | --- |
|
||||
| `api.bytelyst.com/platform/*` | `platform-service:4003` | `public-caddy` | Confirm auth posture |
|
||||
| `api.bytelyst.com/extraction/*` | `extraction-service:4005` | `public-caddy` | Confirm auth posture |
|
||||
| `api.bytelyst.com/mcp/*` | `mcp-server:4007` | `public-caddy` | Confirm public need |
|
||||
| `api.bytelyst.com/peakpulse/*` | `peakpulse-backend:4010` | `public-caddy` | Confirm direct host port can close |
|
||||
| `api.bytelyst.com/chronomind/*` | `chronomind-backend:4011` | `public-caddy` | Confirm direct host port can close |
|
||||
| `api.bytelyst.com/jarvisjr/*` | `jarvisjr-backend:4012` | `public-caddy` | Confirm direct host port can close |
|
||||
| `api.bytelyst.com/nomgap/*` | `nomgap-backend:4013` | `public-caddy` | Confirm direct host port can close |
|
||||
| `api.bytelyst.com/mindlyst/*` | `mindlyst-backend:4014` | `public-caddy` | Confirm direct host port can close |
|
||||
| `api.bytelyst.com/lysnrai/*` | `lysnrai-backend:4015` | `public-caddy` | Confirm direct host port can close |
|
||||
| `api.bytelyst.com/notelett/*` | `notelett-backend:4016` | `public-caddy` | Confirm direct host port can close |
|
||||
| `api.bytelyst.com/flowmonk/*` | `flowmonk-backend:4017` | `public-caddy` | Confirm direct host port can close |
|
||||
| `api.bytelyst.com/actiontrail/*` | `actiontrail-backend:4020` | `public-caddy` | Confirm direct host port can close |
|
||||
| `api.bytelyst.com/localmemgpt/*` | `localmemgpt-backend:4019` | `public-caddy` | Confirm direct host port can close |
|
||||
| `api.bytelyst.com/invttrdg/*` | `invttrdg-backend:4018` | `public-caddy` | Confirm direct host port can close |
|
||||
| `api.bytelyst.com/devops/*` | `devops-backend:4004` | `private-admin` | Should require auth/private access |
|
||||
| `gitea.bytelyst.com` | `gitea-npm-registry:3000` | `public-caddy` | Confirm direct `3300` can close |
|
||||
| `admin.bytelyst.com` | `admin-web:3001` | `private-admin` | Confirm route still resolves; upstream container not in current `docker ps` |
|
||||
| `devops.bytelyst.com` | `devops-web:3000` | `private-admin` | Should require auth/private access |
|
||||
| `tracker.bytelyst.com` | `tracker-web:3003` | `public-caddy` | Confirm direct host port can close |
|
||||
| `llmlab.bytelyst.com` | `llmlab-dashboard:3075` | `private-admin` | Dashboard currently unhealthy; decide public/private/retire |
|
||||
| `ollama.bytelyst.com` | `172.17.0.1:11434` | `private-admin` | Model endpoint should not be unauthenticated public |
|
||||
| `trading-api.bytelyst.com` | `trading-backend:5000` | `public-caddy` | Confirm auth posture |
|
||||
| `invttrdg.bytelyst.com` | `invttrdg-web:3085` | `public-caddy` | Confirm direct host port can close |
|
||||
| `notes.bytelyst.com` | `notelett-web:3045` | `public-caddy` | Confirm direct host port can close |
|
||||
| `clock.bytelyst.com` | `chronomind-web:3030` | `public-caddy` | Confirm direct host port can close |
|
||||
|
||||
## Public Bind Inventory
|
||||
|
||||
These listeners were bound on `0.0.0.0` and/or `[::]` during review.
|
||||
|
||||
| Port | Service/container | Owner / Compose source | Current route | Initial class | Proposed action |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| `22` | `sshd` | host systemd | direct SSH | `public-direct` | Keep public only after SSH key hardening |
|
||||
| `80`, `443` | `caddy` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | public ingress | `public-caddy` | Keep public |
|
||||
| `3000` | `notelett-web` | `/opt/bytelyst/learning_ai_notes/docker-compose.yml` | `notes.bytelyst.com` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke |
|
||||
| `3002` | `lysnrai-dashboard` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `needs-decision` | Private/admin or retire direct exposure |
|
||||
| `3003` | `tracker-web` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `tracker.bytelyst.com` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke |
|
||||
| `3030` | `chronomind-web` | `/root/bytelyst.ai/repos/learning_ai_clock/docker-compose.yml` | `clock.bytelyst.com` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke |
|
||||
| `3035` | `jarvisjr-web` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `needs-decision` | Unhealthy; classify as private/admin or retire |
|
||||
| `3040` | `flowmonk-web` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `needs-decision` | Unhealthy; classify as private/admin or retire |
|
||||
| `3049` | `devops-web` | `/opt/bytelyst/bytelyst-devops-tools/dashboard/docker-compose.yml` | `devops.bytelyst.com` | `private-admin` with direct bypass | Fix old repo path drift, then bind loopback/private |
|
||||
| `3050` | `mindlyst-web` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `needs-decision` | Unhealthy; classify as private/admin or retire |
|
||||
| `3055` | `nomgap-web` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `needs-decision` | Unhealthy; classify as private/admin or retire |
|
||||
| `3060` | `actiontrail-web` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `needs-decision` | Unhealthy; classify as private/admin or retire |
|
||||
| `3070` | `localmemgpt-web` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `needs-decision` | Unhealthy; classify as private/admin or retire |
|
||||
| `3075` | `llmlab-dashboard` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `llmlab.bytelyst.com` | `private-admin` with direct bypass | Dashboard unhealthy; gate or retire |
|
||||
| `3085` | `invttrdg-web` | `/opt/bytelyst/learning_ai_invt_trdg/docker-compose.yml` | `invttrdg.bytelyst.com` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke |
|
||||
| `3100` | `loki` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `private-admin` | Remove public host bind; keep Docker/internal or Tailscale only |
|
||||
| `3300` | `gitea-npm-registry` | non-Compose container labels absent | `gitea.bytelyst.com` | `public-caddy` with direct bypass | Bind loopback or private; keep Caddy route |
|
||||
| `4004` | `devops-backend` | `/opt/bytelyst/learning_ai_devops_tools/dashboard/docker-compose.yml` | `api.bytelyst.com/devops/*` | `private-admin` with direct bypass | Bind loopback/private |
|
||||
| `4010` | `peakpulse-backend` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `api.bytelyst.com/peakpulse/*` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke |
|
||||
| `4011` | `chronomind-backend` | `/root/bytelyst.ai/repos/learning_ai_clock/docker-compose.yml` | `api.bytelyst.com/chronomind/*` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke |
|
||||
| `4012` | `jarvisjr-backend` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `api.bytelyst.com/jarvisjr/*` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke |
|
||||
| `4013` | `nomgap-backend` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `api.bytelyst.com/nomgap/*` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke |
|
||||
| `4014` | `mindlyst-backend` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `api.bytelyst.com/mindlyst/*` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke |
|
||||
| `4015` | `lysnrai-backend` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `api.bytelyst.com/lysnrai/*` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke |
|
||||
| `4016` | `notelett-backend` | `/opt/bytelyst/learning_ai_notes/docker-compose.yml` | `api.bytelyst.com/notelett/*` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke |
|
||||
| `4017` | `flowmonk-backend` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `api.bytelyst.com/flowmonk/*` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke |
|
||||
| `4019` | `localmemgpt-backend` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `api.bytelyst.com/localmemgpt/*` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke |
|
||||
| `4020` | `actiontrail-backend` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `api.bytelyst.com/actiontrail/*` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke |
|
||||
| `4025` | `invttrdg-backend` | `/opt/bytelyst/learning_ai_invt_trdg/docker-compose.yml` | `api.bytelyst.com/invttrdg/*` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke |
|
||||
| `1025` | `mailpit` SMTP | `/root/bytelyst.ai/repos/learning_ai_common_plat/docker-compose.yml` | none found in Caddy | `private-admin` / `docker-internal` | Remove public host bind |
|
||||
| `8025` | `mailpit` UI | `/root/bytelyst.ai/repos/learning_ai_common_plat/docker-compose.yml` | none found in Caddy | `private-admin` | Bind loopback/Tailscale or remove |
|
||||
| `10000` | `azurite` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `docker-internal` | Remove public host bind |
|
||||
| `1234`, `8081` | `cosmos-emulator` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `docker-internal` | Remove public host bind |
|
||||
| `11434` | `ollama` host process | host service | `ollama.bytelyst.com` | `private-admin` | Bind loopback/private or auth-gate; do not leave raw public |
|
||||
|
||||
## Non-Public / Internal Listeners
|
||||
|
||||
| Address/port | Process/service | Initial class | Notes |
|
||||
| --- | --- | --- | --- |
|
||||
| `127.0.0.53:53`, `127.0.0.54:53` | `systemd-resolve` | host-internal | Expected resolver listeners |
|
||||
| `127.0.0.1:44561` | `ollama` | host-internal | Secondary loopback listener observed |
|
||||
| `100.87.53.10:9119`, `100.87.53.10:9120` | `hermes` | private-admin | Tailscale-only bind; keep private |
|
||||
| `100.87.53.10:51855`, `[fd7a:115c:a1e0::3c33:350a]:43379` | `tailscaled` | private-admin | Tailscale control/data listeners |
|
||||
| Docker-internal only | `platform-service`, `mcp-server`, `extraction-service`, `prometheus`, `cadvisor`, `node-exporter`, `valkey`, `trading-backend` | docker-internal/private | No direct host bind seen, except Caddy may route to some by service name |
|
||||
|
||||
## Unhealthy Containers At Inventory Time
|
||||
|
||||
| Container | Port exposure | Initial action |
|
||||
| --- | --- | --- |
|
||||
| `learning_ai_common_plat-llmlab-dashboard-1` | `0.0.0.0:3075` and Caddy `llmlab.bytelyst.com` | Fix/gate/retire before treating public |
|
||||
| `learning_ai_common_plat-actiontrail-web-1` | `0.0.0.0:3060` | Classify and fix/retire |
|
||||
| `learning_ai_common_plat-jarvisjr-web-1` | `0.0.0.0:3035` | Classify and fix/retire |
|
||||
| `learning_ai_common_plat-localmemgpt-web-1` | `0.0.0.0:3070` | Classify and fix/retire |
|
||||
| `learning_ai_common_plat-nomgap-web-1` | `0.0.0.0:3055` | Classify and fix/retire |
|
||||
| `learning_ai_common_plat-flowmonk-web-1` | `0.0.0.0:3040` | Classify and fix/retire |
|
||||
| `learning_ai_common_plat-mindlyst-web-1` | `0.0.0.0:3050` | Classify and fix/retire |
|
||||
|
||||
## Drift / Follow-Up Findings
|
||||
|
||||
- `devops-backend` runs from `/opt/bytelyst/learning_ai_devops_tools/dashboard/docker-compose.yml`.
|
||||
- `devops-web` runs from `/opt/bytelyst/bytelyst-devops-tools/dashboard/docker-compose.yml`, an older path. Align this before changing devops dashboard port bindings.
|
||||
- `gitea-npm-registry` has no Compose labels in Docker inspect output. Find its systemd/compose owner before changing `3300`.
|
||||
- `admin.bytelyst.com` points at `admin-web:3001`, but no `admin-web` container was present in `docker ps` during this inventory.
|
||||
|
||||
## Proposed First Remediation Groups
|
||||
|
||||
Do these in separate commits/windows with smoke checks after each group.
|
||||
|
||||
1. **Internal emulators and mail tools:** `1025`, `8025`, `10000`, `1234`, `8081`.
|
||||
- Expected class: `docker-internal` or `private-admin`.
|
||||
- Preferred fix: remove host port mappings or bind to `127.0.0.1`.
|
||||
2. **Observability internals:** `3100` and any future Prometheus/Grafana/exporter direct binds.
|
||||
- Expected class: `private-admin`.
|
||||
- Preferred fix: Docker-internal or Tailscale-only.
|
||||
3. **Admin/model surfaces:** `11434`, `3075`, `3049`, `4004`.
|
||||
- Expected class: `private-admin`.
|
||||
- Preferred fix: auth gate/private route and no raw public port.
|
||||
4. **Caddy-backed app/API direct bypass ports:** `3000`, `3003`, `3030`, `3085`, `4010`-`4025`.
|
||||
- Expected class: `public-caddy`.
|
||||
- Preferred fix: keep Caddy public, remove raw direct public binds.
|
||||
5. **SSH:** `22`.
|
||||
- Expected class: `public-direct`.
|
||||
- Preferred fix: keep public only after key-only and root-login hardening.
|
||||
|
||||
## Verification Commands
|
||||
|
||||
```bash
|
||||
date -Is
|
||||
ss -ltnp
|
||||
docker ps --format '{{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'
|
||||
docker ps -q | xargs -r docker inspect --format '{{.Name}}\tproject={{index .Config.Labels "com.docker.compose.project"}}\tservice={{index .Config.Labels "com.docker.compose.service"}}\tworkdir={{index .Config.Labels "com.docker.compose.project.working_dir"}}\tconfig={{index .Config.Labels "com.docker.compose.project.config_files"}}'
|
||||
docker exec caddy caddy validate --config /etc/caddy/Caddyfile
|
||||
sed -n '1,260p' /opt/bytelyst/Caddyfile
|
||||
iptables -S DOCKER-USER
|
||||
```
|
||||
@ -377,7 +377,7 @@ Effective `sshd -T` settings showed:
|
||||
### Phase 0 — Freeze and inventory before changes
|
||||
|
||||
- [ ] Freeze new public hostnames/ports until the exposure inventory is complete.
|
||||
- [ ] Generate `docs/vm-exposure-inventory.md` from Docker, Caddy, `ss`, and DNS.
|
||||
- [x] Generate `docs/vm-exposure-inventory.md` from Docker, Caddy, `ss`, and DNS.
|
||||
- [ ] Mark each exposed service as `public`, `private`, `internal-only`, or `retire`.
|
||||
- [ ] Review with S before changing public access for customer/user-facing apps.
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user