Atlas is operational
+This is the temporary Atlas status site. Application routing has not been configured yet.
+diff --git a/config/README.md b/config/README.md
index c785293..a5b8ebc 100644
--- a/config/README.md
+++ b/config/README.md
@@ -41,6 +41,8 @@ Additional service directories may be added when those services are implemented.
`config/sql-server/` contains the version-controlled Docker Compose definition and safe example environment file for the SQL Server container. The live environment file is stored outside the repository at `/etc/atlas/sql-server.env`.
+`config/nginx/` contains the temporary Atlas default HTTP site configuration. Live nginx configuration is deployed under `/etc/nginx/`.
+
## Security Considerations
Configuration templates must not contain live secrets. Secret-bearing live files should be stored outside the repository and referenced by documented paths or environment variable names.
diff --git a/config/nginx/README.md b/config/nginx/README.md
new file mode 100644
index 0000000..fa5a89c
--- /dev/null
+++ b/config/nginx/README.md
@@ -0,0 +1,15 @@
+# nginx Configuration
+
+This directory contains version-controlled nginx configuration for Atlas.
+
+`atlas-default.conf` is the temporary default HTTP site. It serves a small static status page from `/var/www/atlas` and exposes `/health` for local validation.
+
+Deployment locations:
+
+- Source configuration: `config/nginx/atlas-default.conf`
+- Live configuration: `/etc/nginx/sites-available/atlas-default.conf`
+- Enabled site: `/etc/nginx/sites-enabled/atlas-default.conf`
+- Static content: `/var/www/atlas`
+- Health check: `/health`
+
+Do not store TLS private keys, live certificates, secrets, or PlotDirector-specific proxy credentials here.
diff --git a/config/nginx/atlas-default.conf b/config/nginx/atlas-default.conf
new file mode 100644
index 0000000..9b4e155
--- /dev/null
+++ b/config/nginx/atlas-default.conf
@@ -0,0 +1,20 @@
+server {
+ listen 80 default_server;
+ listen [::]:80 default_server;
+
+ server_name _;
+ root /var/www/atlas;
+ index index.html;
+
+ access_log /var/log/nginx/atlas-access.log;
+ error_log /var/log/nginx/atlas-error.log;
+
+ location = /health {
+ add_header Content-Type text/plain;
+ return 200 "ok\n";
+ }
+
+ location / {
+ try_files $uri $uri/ =404;
+ }
+}
diff --git a/config/sql-server/compose.yml b/config/sql-server/compose.yml
index 83c7e28..4031bf9 100644
--- a/config/sql-server/compose.yml
+++ b/config/sql-server/compose.yml
@@ -8,7 +8,7 @@ services:
env_file:
- /etc/atlas/sql-server.env
ports:
- - "1433:1433"
+ - "192.168.10.10:1433:1433"
volumes:
- /sql/mssql:/var/opt/mssql
healthcheck:
@@ -26,4 +26,3 @@ services:
options:
max-size: "10m"
max-file: "5"
-
diff --git a/config/sql-server/sql-server.env.example b/config/sql-server/sql-server.env.example
index 21f54ea..77c32aa 100644
--- a/config/sql-server/sql-server.env.example
+++ b/config/sql-server/sql-server.env.example
@@ -8,4 +8,3 @@ ACCEPT_EULA=Y
MSSQL_PID=Developer
MSSQL_TCP_PORT=1433
MSSQL_SA_PASSWORD=CHANGE_ME_DO_NOT_COMMIT
-
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
index 32ab332..c406932 100644
--- a/docs/ARCHITECTURE.md
+++ b/docs/ARCHITECTURE.md
@@ -34,7 +34,9 @@ The intended model separates operating system state, SQL data, and development d
- Primary network interface: `enp3s0`
- Gitea SSH remote for this repository: `git@git.catherinelynwood.com:atlas-bot/AI-Development-Server.git`
-DNS, firewall, public exposure, and TLS policy are `TODO: Confirm during the relevant implementation phase.`
+UFW is active with default deny incoming and default allow outgoing. Inbound SSH `22`, HTTP `80`, and HTTPS `443` are allowed. SQL Server `1433` is allowed only from `192.168.10.0/24` and is bound to `192.168.10.10`.
+
+Public DNS, TLS, and router exposure policy are `TODO: Confirm during the relevant implementation phase.`
### Repository and Source-Control Flow
@@ -95,14 +97,16 @@ Specific PlotDirector service names, ports, deployment paths, database names, an
### Role of nginx
-nginx is planned as the likely local reverse proxy for HTTP and HTTPS applications. Its future responsibilities may include:
+nginx is installed from the standard Ubuntu 26.04 repositories and currently serves a temporary Atlas HTTP status site.
-- Routing requests to application services.
-- TLS termination.
-- Static file serving where appropriate.
-- Request size, timeout, and header policy.
+- Package version: nginx `1.28.3`
+- Service: `nginx`
+- Site configuration: `/etc/nginx/sites-available/atlas-default.conf`
+- Version-controlled source: `config/nginx/atlas-default.conf`
+- Static site root: `/var/www/atlas`
+- Health endpoint: `http://192.168.10.10/health`
-nginx installation, site layout, TLS certificate handling, and exposure rules are `TODO: Confirm during the relevant implementation phase.`
+HTTPS, TLS certificates, and PlotDirector reverse-proxy configuration are not configured yet.
### Role of SQL Server
@@ -185,8 +189,8 @@ flowchart TB
Tests[Future smoke tests
tests/]
SQL[(SQL data volume
/sql)]
Deploy[Future deployed applications]
- Nginx[Future nginx reverse proxy]
- Docker[Future Docker runtime]
+ Nginx[nginx reverse proxy]
+ Docker[Docker runtime]
end
Repo --> Install
diff --git a/docs/BUILD-LOG.md b/docs/BUILD-LOG.md
index bd276fb..e758add 100644
--- a/docs/BUILD-LOG.md
+++ b/docs/BUILD-LOG.md
@@ -10,6 +10,59 @@ This file records chronological Atlas build and infrastructure milestones. Do no
### Phase
+Phase 2 nginx and firewall baseline.
+
+### Objective
+
+Install nginx, deploy a minimal Atlas status site, restrict SQL Server binding, and enable a safe UFW baseline.
+
+### Changes
+
+- Added `install/phases/40-nginx.sh`.
+- Installed nginx `1.28.3` from Ubuntu `resolute-updates`.
+- Added Atlas default nginx site under `config/nginx/`.
+- Deployed static site content to `/var/www/atlas`.
+- Enabled `/health` endpoint.
+- Enabled UFW with default deny incoming and default allow outgoing.
+- Allowed inbound TCP `22`, `80`, and `443`.
+- Allowed inbound TCP `1433` only from `192.168.10.0/24`.
+- Changed SQL Server container binding to `192.168.10.10:1433:1433`.
+
+### Validation
+
+- `nginx -t` passed.
+- HTTP checks passed for `127.0.0.1` and `192.168.10.10`.
+- `/health` returned HTTP 200.
+- nginx restart test passed.
+- SQL Server remained healthy after container recreation.
+- SQL data persisted after container recreation.
+- SQL LAN connection test passed.
+- Atlas discovery and validation passed.
+
+### Problems
+
+- Initial nginx deployment encountered a duplicate default-server config while the Ubuntu default site was still enabled. The installer now validates before and after disabling the default site.
+
+### Decisions
+
+- Keep nginx HTTP-only until a later TLS phase.
+- Keep the temporary Atlas status site separate from future PlotDirector proxy configuration.
+- Do not rely on UFW alone for Docker-published SQL Server; bind SQL Server to the Atlas LAN address.
+
+### Git Commit
+
+Not yet committed.
+
+### Operator
+
+Codex.
+
+### Date
+
+2026-07-10
+
+### Phase
+
Phase 2 Docker and SQL Server container platform.
### Objective
diff --git a/docs/REBUILD-GUIDE.md b/docs/REBUILD-GUIDE.md
index d36264d..f0ac66a 100644
--- a/docs/REBUILD-GUIDE.md
+++ b/docs/REBUILD-GUIDE.md
@@ -11,6 +11,7 @@ This guide describes the planned rebuild path for Atlas. At Phase 1, the reposit
- .NET installation script: present.
- Docker installation script: present.
- SQL Server container installation script: present.
+- nginx installation script: present.
- Other component installation scripts: not yet created.
- Configuration templates: not yet created.
- Operational scripts: not yet created.
@@ -118,8 +119,10 @@ Planned bootstrap flow:
7. Run `install/phases/10-dotnet.sh` when .NET SDKs are required.
8. Run `install/phases/30-docker.sh`.
9. Run `install/phases/20-sql-server.sh` after Docker is available.
-10. Run additional ordered installation scripts from `install/phases/` when they exist.
-11. Run smoke tests from `tests/` when they exist.
+10. Run `install/phases/40-nginx.sh`.
+11. Apply the UFW baseline rules.
+12. Run additional ordered installation scripts from `install/phases/` when they exist.
+13. Run smoke tests from `tests/` when they exist.
Bootstrap command sequence is `TODO: Confirm during the relevant implementation phase.`
@@ -154,7 +157,22 @@ sudo docker inspect -f '{{.State.Health.Status}}' atlas-sql-server
SQL Server live secrets are stored at `/etc/atlas/sql-server.env` and must not be committed.
-These scripts do not exist yet. When created, each script must:
+Current nginx validation commands:
+
+```bash
+sudo nginx -t
+systemctl is-active nginx
+curl -fsS http://127.0.0.1/health
+curl -fsS http://192.168.10.10/health
+```
+
+Current firewall inspection command:
+
+```bash
+sudo ufw status numbered
+```
+
+Installation scripts must:
- Be idempotent.
- Validate prerequisites.
diff --git a/install/README.md b/install/README.md
index 9bbbf76..21419e8 100644
--- a/install/README.md
+++ b/install/README.md
@@ -54,6 +54,7 @@ Current Phase 2 installer:
install/phases/10-dotnet.sh
install/phases/20-sql-server.sh
install/phases/30-docker.sh
+install/phases/40-nginx.sh
```
The .NET installer installs only the required .NET SDK packages and verifies them with `dotnet --list-sdks`.
@@ -67,6 +68,8 @@ Current installed SDKs:
Docker is installed from Docker's official signed Ubuntu APT repository. SQL Server uses Microsoft's official SQL Server 2025 container image because native SQL Server packages are not supported on Ubuntu 26.04.
+nginx is installed from Ubuntu repositories and serves the temporary Atlas status site until PlotDirector routing is configured.
+
Runtime output belongs outside the repository:
- `/var/log/atlas/install/`
diff --git a/install/phases/40-nginx.sh b/install/phases/40-nginx.sh
new file mode 100755
index 0000000..920a9d6
--- /dev/null
+++ b/install/phases/40-nginx.sh
@@ -0,0 +1,167 @@
+#!/usr/bin/env bash
+set -Eeuo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+INSTALL_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
+REPO_ROOT="$(cd "${INSTALL_DIR}/.." && pwd)"
+
+# shellcheck source=install/lib/common.sh
+source "${INSTALL_DIR}/lib/common.sh"
+# shellcheck source=install/lib/logging.sh
+source "${INSTALL_DIR}/lib/logging.sh"
+# shellcheck source=install/lib/packages.sh
+source "${INSTALL_DIR}/lib/packages.sh"
+
+atlas_set_script_name "40-nginx.sh"
+atlas_install_error_trap
+
+ATLAS_SITE_ROOT="/var/www/atlas"
+ATLAS_SITE_CONF_SOURCE="${REPO_ROOT}/config/nginx/atlas-default.conf"
+ATLAS_SITE_CONF_DEST="/etc/nginx/sites-available/atlas-default.conf"
+ATLAS_SITE_LINK="/etc/nginx/sites-enabled/atlas-default.conf"
+NGINX_DEFAULT_LINK="/etc/nginx/sites-enabled/default"
+NGINX_BACKUP_DIR="/etc/nginx/atlas-backups"
+
+usage() {
+ cat <<'USAGE'
+Usage: install/phases/40-nginx.sh [options]
+
+Install nginx from Ubuntu repositories and deploy the temporary Atlas default
+HTTP status site. This does not configure HTTPS or PlotDirector proxying.
+
+Options:
+ --help Show this help text.
+ --version Show script/framework version.
+ --dry-run Show intended actions without making changes.
+ --verbose Print additional diagnostic output.
+
+USAGE
+}
+
+while [[ "$#" -gt 0 ]]; do
+ case "$1" in
+ --help)
+ usage
+ exit "${ATLAS_EXIT_OK}"
+ ;;
+ --version)
+ atlas_print_version
+ exit "${ATLAS_EXIT_OK}"
+ ;;
+ --dry-run)
+ atlas_enable_dry_run
+ ;;
+ --verbose)
+ atlas_enable_verbose
+ ;;
+ *)
+ atlas_usage_error "Unknown option: $1"
+ exit "${ATLAS_EXIT_USAGE}"
+ ;;
+ esac
+ shift
+done
+
+install_nginx() {
+ if atlas_package_installed nginx; then
+ atlas_log_ok "nginx is already installed"
+ else
+ atlas_install_packages nginx
+ fi
+}
+
+write_site_content() {
+ local tmp_file
+ tmp_file="$(mktemp)"
+ cat >"$tmp_file" <<'HTML'
+
+
+
This is the temporary Atlas status site. Application routing has not been configured yet.
+