diff --git a/10-systems/applications/gnexus-book.md b/10-systems/applications/gnexus-book.md new file mode 100644 index 0000000..fbd0a50 --- /dev/null +++ b/10-systems/applications/gnexus-book.md @@ -0,0 +1,36 @@ +--- +owner: gmikcon +status: active +last_reviewed: 2026-05-10 +review_interval: 30d +confidence: high +source_of_truth: repository +--- + +# Gnexus Book + +Gnexus Book is the canonical infrastructure knowledge base and maintenance API for the personal digital and server infrastructure. + +## Repository + +- Local path: `/home/gmikcon/Projects/gnexus-book`. +- Source of truth: Markdown, YAML inventory, JSON Schema, and Git history. + +## Runtime Components + +- Backend: FastAPI under `server/`. +- UI: Vue/Vite under `ui/`. +- UI dependency: `gnexus-ui-kit`. + +## Current Local Endpoints + +- Backend API: `http://127.0.0.1:3005/`. +- API docs: `http://127.0.0.1:3005/api-docs`. +- UI: `http://127.0.0.1:3006/`. +- LAN UI: `http://192.168.1.75:3006/`. + +## Maintenance Model + +Agents should prefer structured inventory changes, validation, pending changes, and local commits through the maintenance API. + +No raw credentials are documented here. diff --git a/10-systems/applications/gnexus-ui-kit.md b/10-systems/applications/gnexus-ui-kit.md new file mode 100644 index 0000000..e6fa6dc --- /dev/null +++ b/10-systems/applications/gnexus-ui-kit.md @@ -0,0 +1,28 @@ +--- +owner: gmikcon +status: active +last_reviewed: 2026-05-10 +review_interval: 90d +confidence: medium +source_of_truth: owner-confirmed-and-local-repository +--- + +# Gnexus UI Kit + +Gnexus UI Kit is the official UI library and visual style foundation for Gnexus projects. + +## Repository + +- Git URL: `https://git.gnexus.space/root/gnexus-ui-kit`. +- Local path: `/home/gmikcon/Projects/gnexus-ui-kit`. + +## Role + +Gnexus Book uses this project as a dependency for the Vue UI. The dependency should remain updateable so visual style changes can be managed centrally. + +## Documentation + +- Local docs entrypoint: `/home/gmikcon/Projects/gnexus-ui-kit/docs/index.md`. +- Vue adapter docs live under `/home/gmikcon/Projects/gnexus-ui-kit/docs/vue/`. + +No raw credentials are documented here. diff --git a/40-inventory/endpoints.yml b/40-inventory/endpoints.yml new file mode 100644 index 0000000..8618621 --- /dev/null +++ b/40-inventory/endpoints.yml @@ -0,0 +1,79 @@ +# Addressable URLs, management UIs, APIs, ports, and other access points. +--- +- id: hp-proliant-cockpit + name: HP ProLiant Cockpit + type: management-ui + status: active + exposure: local + url: https://192.168.1.130:9090/system + protocol: https + host: 192.168.1.130 + port: 9090 + owner_host: hp-proliant-dl380-g6 + purpose: Host administration through Cockpit. + auth: required + docs: ../10-systems/hardware/hp-proliant-dl380-g6.md + last_reviewed: 2026-05-09 + source_of_truth: owner-confirmed + +- id: pfsense-web-ui + name: pfSense Web UI + type: management-ui + status: active + exposure: local + url: https://192.168.1.1/ + protocol: https + host: 192.168.1.1 + owner_host: pfsense-router + purpose: Local router and firewall administration. + auth: required + docs: ../10-systems/networks/pfsense-router.md + last_reviewed: 2026-05-09 + source_of_truth: owner-confirmed + +- id: external-vps-http + name: External VPS HTTP endpoint + type: public-web + status: active + exposure: public + url: http://194.61.53.43/ + protocol: http + host: 194.61.53.43 + port: 80 + owner_host: external-vps + purpose: Public HTTP endpoint on the external VPS. + docs: ../10-systems/servers/external-vps.md + last_reviewed: 2026-05-09 + source_of_truth: curl-observed + +- id: gnexus-space-https + name: gnexus.space HTTPS endpoint + type: public-web + status: active + exposure: public + url: https://gnexus.space/ + protocol: https + host: gnexus.space + port: 443 + domain: gnexus-space + service: internal-nginx-proxy + owner_host: external-vps + purpose: Public HTTPS endpoint for the primary domain. + docs: ../10-systems/traffic-routes/public-gnexus-space-to-internal-nginx.md + last_reviewed: 2026-05-09 + source_of_truth: curl-observed-and-ssh-nginx + +- id: internal-proxy-ssh + name: Internal Proxy VPS SSH + type: ssh + status: active + exposure: local + protocol: ssh + host: 192.168.1.226 + port: 22 + owner_host: internal-proxy-vps + purpose: SSH administration endpoint for the internal proxy VPS. + auth: required + docs: ../10-systems/servers/internal-proxy-vps.md + last_reviewed: 2026-05-09 + source_of_truth: ssh-host diff --git a/40-inventory/integrations.yml b/40-inventory/integrations.yml new file mode 100644 index 0000000..1efcf22 --- /dev/null +++ b/40-inventory/integrations.yml @@ -0,0 +1,55 @@ +# Cross-entity relationships that are not owned by a single host, service, or route. +--- +- id: external-vps-to-internal-proxy-openvpn + name: External VPS to internal proxy over OpenVPN + type: vpn-link + status: active + source: external-vps + target: internal-proxy-vps + via: + - external-openvpn-server + - openvpn-tunnel + - internal-openvpn-client + protocols: + - openvpn + direction: external-to-internal + purpose: Carry public entrypoint traffic from the external VPS into the home infrastructure. + confidence: high + docs: ../10-systems/traffic-routes/public-gnexus-space-to-internal-nginx.md + last_reviewed: 2026-05-09 + source_of_truth: owner-confirmed + +- id: gnexus-domain-family-to-internal-nginx + name: gnexus.space domain family to internal nginx + type: reverse-proxy-routing + status: active + source: gnexus-space + target: internal-nginx-proxy + via: + - external-vps + - openvpn-tunnel + - internal-proxy-vps + protocols: + - http + - https + ports: + - 80 + - 443 + direction: public-to-internal + purpose: Route gnexus.space and subdomains to internal services through nginx. + confidence: high + docs: ../10-systems/traffic-routes/public-gnexus-space-to-internal-nginx.md + last_reviewed: 2026-05-09 + source_of_truth: owner-confirmed-and-ssh-nginx + +- id: gnexus-book-uses-gnexus-ui-kit + name: Gnexus Book uses Gnexus UI Kit + type: project-dependency + status: active + source: gnexus-book + target: gnexus-ui-kit + purpose: Keep the documentation UI aligned with the official Gnexus visual style. + confidence: high + docs: ../10-systems/applications/gnexus-book.md + last_reviewed: 2026-05-10 + source_of_truth: repository-package-json diff --git a/40-inventory/projects.yml b/40-inventory/projects.yml new file mode 100644 index 0000000..5c6bc16 --- /dev/null +++ b/40-inventory/projects.yml @@ -0,0 +1,49 @@ +# Software projects, repositories, external documentation, and deployment links. +--- +- id: gnexus-book + name: Gnexus Book + status: active + type: documentation-system + description: Knowledge base and maintenance API for personal digital and server infrastructure. + repositories: + - name: gnexus-book + path: /home/gmikcon/Projects/gnexus-book + role: canonical-documentation-repository + documentation: + - label: Project implementation plan + path: ../00-overview/mvp-implementation-plan.md + role: planning + - label: Documentation rules + path: ../90-maintenance/documentation-rules.md + role: maintenance-policy + related_services: [] + related_hosts: + - internal-proxy-vps + - external-vps + related_domains: + - gnexus-space + docs: ../10-systems/applications/gnexus-book.md + last_reviewed: 2026-05-10 + source_of_truth: repository + +- id: gnexus-ui-kit + name: Gnexus UI Kit + status: active + type: ui-library + description: Official Gnexus UI kit and visual style foundation. + repositories: + - name: gnexus-ui-kit + url: https://git.gnexus.space/root/gnexus-ui-kit + path: /home/gmikcon/Projects/gnexus-ui-kit + role: source-repository + documentation: + - label: UI kit docs + path: /home/gmikcon/Projects/gnexus-ui-kit/docs/index.md + role: upstream-docs + related_services: [] + related_hosts: [] + related_domains: + - git-gnexus-space + docs: ../10-systems/applications/gnexus-ui-kit.md + last_reviewed: 2026-05-10 + source_of_truth: owner-confirmed-and-local-repository diff --git a/90-maintenance/documentation-rules.md b/90-maintenance/documentation-rules.md index d7c4c4c..c66884e 100644 --- a/90-maintenance/documentation-rules.md +++ b/90-maintenance/documentation-rules.md @@ -51,6 +51,16 @@ Inventory item `id` values must be unique within each inventory file. +Use the broadest durable inventory type that matches the fact: + +- `hosts.yml` for operating-system environments and machines; +- `services.yml` for applications, daemons, reverse proxies, workers, and infrastructure services; +- `endpoints.yml` for URLs, APIs, management UIs, SSH ports, local ports, and other addressable access points; +- `integrations.yml` for dependencies and cross-entity relationships that do not belong to a single record; +- `projects.yml` for repositories, project documentation, deployment links, and project-to-infrastructure references. + +Do not create a narrowly specialized inventory type until the same shape appears repeatedly and cannot be represented cleanly by existing broad types. + Run `GET /validate` before applying or committing documentation changes. A clean report means: - inventory files pass JSON Schema validation; @@ -60,3 +70,5 @@ - no obvious raw secrets were detected. Use `GET /relationships` when an agent needs the current infrastructure graph. This endpoint is intentionally read-only and returns unresolved references separately from validation errors, so partially documented nodes such as future hosts, external VPS names, or route placeholders can be made visible without blocking incremental documentation work. + +Use [service-documentation-template.md](service-documentation-template.md) when creating or normalizing service, application, smart-home, or project-service documentation. diff --git a/90-maintenance/service-documentation-template.md b/90-maintenance/service-documentation-template.md new file mode 100644 index 0000000..0022b40 --- /dev/null +++ b/90-maintenance/service-documentation-template.md @@ -0,0 +1,69 @@ +--- +owner: gmikcon +status: active +last_reviewed: 2026-05-10 +review_interval: 90d +confidence: medium +source_of_truth: template +--- + +# Service Documentation Template + +Use this structure when documenting an application, infrastructure daemon, smart-home component, project service, or locally hosted tool. + +## Purpose + +Describe what the service does and why it exists. + +## Ownership + +- Owner: +- Criticality: +- Environment: +- Status: + +## Runtime + +- Host or VM: +- Runtime type: +- Process or service name: +- Container name, if applicable: +- Relevant systemd unit, if applicable: + +## Endpoints + +List public, local, VPN, API, management, SSH, and other access points. Prefer linking to `40-inventory/endpoints.yml`. + +## Dependencies + +List databases, queues, APIs, filesystems, DNS records, upstream services, Home Assistant integrations, or project repositories. + +## Data And Storage + +Describe persistent data, important paths, databases, media storage, and backup expectations. + +## Deployment + +Describe deployment source, repository, branch, build/runtime commands, environment files, and restart procedure. + +Do not document raw secret values. + +## Operations + +- Health check: +- Logs: +- Restart: +- Backup: +- Restore: + +## Known Risks + +List stale facts, weak assumptions, missing monitoring, backup gaps, security notes, or migration risks. + +## Related Records + +- Inventory: +- Endpoints: +- Integrations: +- Projects: +- Runbooks: diff --git a/README.md b/README.md index e500d98..cad61ae 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,9 @@ - [Hardware inventory](40-inventory/hardware.yml) - [Virtual machine inventory](40-inventory/virtual-machines.yml) - [Traffic routes](40-inventory/traffic-routes.yml) +- [Endpoints](40-inventory/endpoints.yml) +- [Integrations](40-inventory/integrations.yml) +- [Projects](40-inventory/projects.yml) ## Server diff --git a/schemas/endpoint.schema.json b/schemas/endpoint.schema.json new file mode 100644 index 0000000..cc12d45 --- /dev/null +++ b/schemas/endpoint.schema.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "endpoint.schema.json", + "type": "array", + "items": { + "type": "object", + "required": ["id", "name", "type", "status", "exposure", "docs", "last_reviewed"], + "properties": { + "id": { "type": "string" }, + "name": { "type": "string" }, + "type": { "type": "string" }, + "status": { "type": "string" }, + "exposure": { "type": "string", "enum": ["public", "vpn", "local", "private", "unknown"] }, + "url": { "type": "string" }, + "protocol": { "type": "string" }, + "host": { "type": "string" }, + "port": { "type": "integer" }, + "domain": { "type": "string" }, + "service": { "type": "string" }, + "owner_host": { "type": "string" }, + "purpose": { "type": "string" }, + "auth": { "type": "string" }, + "notes": { "type": "string" }, + "docs": { "type": "string" }, + "last_reviewed": { "type": "string" }, + "source_of_truth": { "type": "string" } + }, + "additionalProperties": true + } +} diff --git a/schemas/integration.schema.json b/schemas/integration.schema.json new file mode 100644 index 0000000..c1820a5 --- /dev/null +++ b/schemas/integration.schema.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "integration.schema.json", + "type": "array", + "items": { + "type": "object", + "required": ["id", "name", "type", "status", "source", "target", "docs", "last_reviewed"], + "properties": { + "id": { "type": "string" }, + "name": { "type": "string" }, + "type": { "type": "string" }, + "status": { "type": "string" }, + "source": { "type": "string" }, + "target": { "type": "string" }, + "via": { "type": "array", "items": { "type": "string" } }, + "protocols": { "type": "array", "items": { "type": "string" } }, + "ports": { "type": "array", "items": { "type": "integer" } }, + "direction": { "type": "string" }, + "purpose": { "type": "string" }, + "confidence": { "type": "string" }, + "docs": { "type": "string" }, + "last_reviewed": { "type": "string" }, + "source_of_truth": { "type": "string" } + }, + "additionalProperties": true + } +} diff --git a/schemas/project.schema.json b/schemas/project.schema.json new file mode 100644 index 0000000..f88ca50 --- /dev/null +++ b/schemas/project.schema.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "project.schema.json", + "type": "array", + "items": { + "type": "object", + "required": ["id", "name", "status", "type", "docs", "last_reviewed"], + "properties": { + "id": { "type": "string" }, + "name": { "type": "string" }, + "status": { "type": "string" }, + "type": { "type": "string" }, + "description": { "type": "string" }, + "repositories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "url": { "type": "string" }, + "path": { "type": "string" }, + "role": { "type": "string" } + }, + "additionalProperties": true + } + }, + "documentation": { + "type": "array", + "items": { + "type": "object", + "properties": { + "label": { "type": "string" }, + "url": { "type": "string" }, + "path": { "type": "string" }, + "role": { "type": "string" } + }, + "additionalProperties": true + } + }, + "related_services": { "type": "array", "items": { "type": "string" } }, + "related_hosts": { "type": "array", "items": { "type": "string" } }, + "related_domains": { "type": "array", "items": { "type": "string" } }, + "docs": { "type": "string" }, + "last_reviewed": { "type": "string" }, + "source_of_truth": { "type": "string" } + }, + "additionalProperties": true + } +} diff --git a/server/README.md b/server/README.md index af96f8d..e1a26a6 100644 --- a/server/README.md +++ b/server/README.md @@ -59,4 +59,6 @@ - duplicate inventory item IDs; - common raw secret assignment patterns in Markdown, YAML, JSON, and dotenv-style files. +Inventory includes broad records for hosts, services, domains, traffic routes, endpoints, integrations, projects, databases, backups, networks, hardware, and virtual machines. + `POST /changes/{id}/apply` and `POST /commit` both rely on validation to block unsafe repository states. diff --git a/server/app/relationships.py b/server/app/relationships.py index f532560..089fc1f 100644 --- a/server/app/relationships.py +++ b/server/app/relationships.py @@ -16,6 +16,9 @@ "traffic-routes": {"source", "entrypoint", "path", "destination", "used_by"}, "databases": {"host", "used_by", "backup_policy"}, "backups": {"target"}, + "endpoints": {"domain", "service", "owner_host"}, + "integrations": {"source", "target", "via"}, + "projects": {"related_services", "related_hosts", "related_domains"}, } @@ -48,6 +51,9 @@ _add_traffic_route_edges(records_by_type, node_by_raw_id, edges, unresolved) _add_database_edges(records_by_type, node_by_raw_id, edges, unresolved) _add_backup_edges(records_by_type, node_by_raw_id, edges, unresolved) + _add_endpoint_edges(records_by_type, node_by_raw_id, edges, unresolved) + _add_integration_edges(records_by_type, node_by_raw_id, edges, unresolved) + _add_project_edges(records_by_type, node_by_raw_id, edges, unresolved) return { "nodes": nodes, @@ -231,6 +237,49 @@ _append_reference(source, record.get("target"), "backs_up", "target", node_by_raw_id, edges, unresolved) +def _add_endpoint_edges( + records_by_type: dict[str, list[dict[str, Any]]], + node_by_raw_id: dict[str, str], + edges: list[RelationshipEdge], + unresolved: list[UnresolvedReference], +) -> None: + for record in records_by_type.get("endpoints", []): + source = _source_node("endpoints", record) + _append_reference(source, record.get("domain"), "endpoint_for_domain", "domain", node_by_raw_id, edges, unresolved) + _append_reference(source, record.get("service"), "endpoint_for_service", "service", node_by_raw_id, edges, unresolved) + _append_reference(source, record.get("owner_host"), "owned_by_host", "owner_host", node_by_raw_id, edges, unresolved) + + +def _add_integration_edges( + records_by_type: dict[str, list[dict[str, Any]]], + node_by_raw_id: dict[str, str], + edges: list[RelationshipEdge], + unresolved: list[UnresolvedReference], +) -> None: + for record in records_by_type.get("integrations", []): + source = _source_node("integrations", record) + _append_reference(source, record.get("source"), "integration_source", "source", node_by_raw_id, edges, unresolved) + _append_reference(source, record.get("target"), "integration_target", "target", node_by_raw_id, edges, unresolved) + for target in _as_list(record.get("via")): + _append_reference(source, target, "integration_via", "via", node_by_raw_id, edges, unresolved) + + +def _add_project_edges( + records_by_type: dict[str, list[dict[str, Any]]], + node_by_raw_id: dict[str, str], + edges: list[RelationshipEdge], + unresolved: list[UnresolvedReference], +) -> None: + for record in records_by_type.get("projects", []): + source = _source_node("projects", record) + for target in _as_list(record.get("related_services")): + _append_reference(source, target, "project_service", "related_services", node_by_raw_id, edges, unresolved) + for target in _as_list(record.get("related_hosts")): + _append_reference(source, target, "project_host", "related_hosts", node_by_raw_id, edges, unresolved) + for target in _as_list(record.get("related_domains")): + _append_reference(source, target, "project_domain", "related_domains", node_by_raw_id, edges, unresolved) + + def _append_reference( source: str, raw_target: object, diff --git a/server/app/validation.py b/server/app/validation.py index d939978..bba28b1 100644 --- a/server/app/validation.py +++ b/server/app/validation.py @@ -20,9 +20,12 @@ "backups": "backup.schema.json", "databases": "database.schema.json", "domains": "domain.schema.json", + "endpoints": "endpoint.schema.json", "hardware": "hardware.schema.json", "hosts": "host.schema.json", + "integrations": "integration.schema.json", "networks": "network.schema.json", + "projects": "project.schema.json", "services": "service.schema.json", "traffic-routes": "traffic-route.schema.json", "virtual-machines": "virtual-machine.schema.json", diff --git a/server/tests/test_api.py b/server/tests/test_api.py index f868f2f..a2729b2 100644 --- a/server/tests/test_api.py +++ b/server/tests/test_api.py @@ -45,6 +45,9 @@ def test_inventory_types_endpoint() -> None: response = list_inventory_types(InventoryRepository(get_settings())) + assert "endpoints" in response + assert "integrations" in response + assert "projects" in response assert "virtual-machines" in response assert "traffic-routes" in response @@ -100,6 +103,21 @@ "target": "services/internal-nginx-proxy", "relation": "destination", } in response["edges"] + assert { + "source": "endpoints/gnexus-space-https", + "target": "domains/gnexus-space", + "relation": "endpoint_for_domain", + } in response["edges"] + assert { + "source": "integrations/gnexus-book-uses-gnexus-ui-kit", + "target": "projects/gnexus-ui-kit", + "relation": "integration_target", + } in response["edges"] + assert { + "source": "projects/gnexus-book", + "target": "hosts/internal-proxy-vps", + "relation": "project_host", + } in response["edges"] assert response["unresolved_references"] == [] diff --git a/server/tests/test_docs_repository.py b/server/tests/test_docs_repository.py index a79c57e..dbbafed 100644 --- a/server/tests/test_docs_repository.py +++ b/server/tests/test_docs_repository.py @@ -21,9 +21,12 @@ "backups", "databases", "domains", + "endpoints", "hardware", "hosts", + "integrations", "networks", + "projects", "services", "traffic-routes", "virtual-machines", diff --git a/server/tests/test_git_adapter.py b/server/tests/test_git_adapter.py index 7942c54..23cfb89 100644 --- a/server/tests/test_git_adapter.py +++ b/server/tests/test_git_adapter.py @@ -17,9 +17,12 @@ "backups", "databases", "domains", + "endpoints", "hardware", "hosts", + "integrations", "networks", + "projects", "services", "traffic-routes", "virtual-machines", diff --git a/server/tests/test_pending_changes.py b/server/tests/test_pending_changes.py index 40937f1..4b78a15 100644 --- a/server/tests/test_pending_changes.py +++ b/server/tests/test_pending_changes.py @@ -35,9 +35,12 @@ "backups", "databases", "domains", + "endpoints", "hardware", "hosts", + "integrations", "networks", + "projects", "services", "traffic-routes", "virtual-machines",