CD/CD consolidation
This commit is contained in:
181
devops/ansible/README.md
Normal file
181
devops/ansible/README.md
Normal file
@@ -0,0 +1,181 @@
|
||||
# Zastava Agent Ansible Deployment
|
||||
|
||||
Ansible playbook for deploying StellaOps Zastava Agent on VM/bare-metal hosts.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Ansible 2.10 or later
|
||||
- Target hosts must have:
|
||||
- Docker installed and running
|
||||
- SSH access with sudo privileges
|
||||
- systemd as init system
|
||||
- Internet access (for downloading agent binaries) OR local artifact repository
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. **Create inventory file:**
|
||||
|
||||
```bash
|
||||
cp inventory.yml.sample inventory.yml
|
||||
```
|
||||
|
||||
2. **Edit inventory with your hosts and configuration:**
|
||||
|
||||
```yaml
|
||||
zastava_agents:
|
||||
hosts:
|
||||
your-host:
|
||||
ansible_host: 192.168.1.100
|
||||
ansible_user: ubuntu
|
||||
vars:
|
||||
zastava_tenant: your-tenant
|
||||
scanner_backend_url: https://scanner.internal
|
||||
```
|
||||
|
||||
3. **Run the playbook:**
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventory.yml zastava-agent.yml
|
||||
```
|
||||
|
||||
## Configuration Variables
|
||||
|
||||
### Required Variables
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `zastava_tenant` | Tenant identifier for multi-tenancy isolation |
|
||||
| `scanner_backend_url` | URL of the Scanner backend service |
|
||||
|
||||
### Optional Variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `zastava_version` | `latest` | Agent version to deploy |
|
||||
| `zastava_node_name` | hostname | Override node name in events |
|
||||
| `zastava_health_port` | `8080` | Health check HTTP port |
|
||||
| `docker_socket` | `/var/run/docker.sock` | Docker socket path |
|
||||
| `zastava_log_level` | `Information` | Serilog log level |
|
||||
| `scanner_backend_insecure` | `false` | Allow HTTP backend (NOT for production) |
|
||||
| `download_base_url` | `https://releases.stellaops.org` | Base URL for agent downloads |
|
||||
|
||||
### Advanced Variables
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `zastava_extra_env` | Dictionary of additional environment variables |
|
||||
|
||||
## Directory Structure
|
||||
|
||||
After deployment, the agent is installed with the following structure:
|
||||
|
||||
```
|
||||
/opt/stellaops/zastava-agent/ # Agent binaries
|
||||
/etc/stellaops/zastava-agent.env # Environment configuration
|
||||
/var/lib/zastava-agent/ # Data directory
|
||||
/var/lib/zastava-agent/runtime-events/ # Event buffer (disk-backed)
|
||||
/etc/systemd/system/zastava-agent.service # systemd unit
|
||||
```
|
||||
|
||||
## Post-Deployment Verification
|
||||
|
||||
### Check Service Status
|
||||
|
||||
```bash
|
||||
systemctl status zastava-agent
|
||||
```
|
||||
|
||||
### View Logs
|
||||
|
||||
```bash
|
||||
journalctl -u zastava-agent -f
|
||||
```
|
||||
|
||||
### Health Endpoints
|
||||
|
||||
| Endpoint | Description |
|
||||
|----------|-------------|
|
||||
| `/healthz` | Liveness probe - agent is running |
|
||||
| `/readyz` | Readiness probe - agent can process events |
|
||||
| `/livez` | Alias for liveness probe |
|
||||
|
||||
```bash
|
||||
curl http://localhost:8080/healthz
|
||||
curl http://localhost:8080/readyz
|
||||
```
|
||||
|
||||
## Air-Gapped Deployment
|
||||
|
||||
For air-gapped environments:
|
||||
|
||||
1. Download agent tarball to a local artifact server
|
||||
2. Set `download_base_url` to your local server:
|
||||
|
||||
```yaml
|
||||
download_base_url: https://artifacts.internal/stellaops
|
||||
```
|
||||
|
||||
3. Ensure the URL structure matches:
|
||||
`{download_base_url}/zastava-agent/{version}/zastava-agent-linux-{arch}.tar.gz`
|
||||
|
||||
## Security Notes
|
||||
|
||||
### Docker Socket Access
|
||||
|
||||
The agent requires read access to the Docker socket to monitor container events.
|
||||
The service runs as the `zastava-agent` user in the `docker` group.
|
||||
|
||||
See `docs/modules/zastava/operations/docker-socket-permissions.md` for security
|
||||
considerations and alternative configurations.
|
||||
|
||||
### systemd Hardening
|
||||
|
||||
The service unit includes security hardening:
|
||||
|
||||
- `NoNewPrivileges=true` - Prevent privilege escalation
|
||||
- `ProtectSystem=strict` - Read-only system directories
|
||||
- `PrivateTmp=true` - Isolated /tmp
|
||||
- `ProtectKernelTunables=true` - No kernel parameter modification
|
||||
- Resource limits on file descriptors and memory
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Agent Won't Start
|
||||
|
||||
1. Check Docker service: `systemctl status docker`
|
||||
2. Verify Docker socket permissions: `ls -la /var/run/docker.sock`
|
||||
3. Check agent logs: `journalctl -u zastava-agent -e`
|
||||
|
||||
### Cannot Connect to Backend
|
||||
|
||||
1. Verify network connectivity: `curl -I ${scanner_backend_url}/healthz`
|
||||
2. Check TLS certificates if using HTTPS
|
||||
3. Ensure firewall allows outbound connections
|
||||
|
||||
### Events Not Being Sent
|
||||
|
||||
1. Check event buffer directory permissions
|
||||
2. Verify health endpoint returns healthy: `curl localhost:8080/readyz`
|
||||
3. Check agent logs for connection errors
|
||||
|
||||
## Uninstallation
|
||||
|
||||
To remove the agent:
|
||||
|
||||
```bash
|
||||
# Stop and disable service
|
||||
sudo systemctl stop zastava-agent
|
||||
sudo systemctl disable zastava-agent
|
||||
|
||||
# Remove files
|
||||
sudo rm -rf /opt/stellaops/zastava-agent
|
||||
sudo rm -f /etc/stellaops/zastava-agent.env
|
||||
sudo rm -f /etc/systemd/system/zastava-agent.service
|
||||
sudo rm -rf /var/lib/zastava-agent
|
||||
|
||||
# Remove user
|
||||
sudo userdel zastava-agent
|
||||
|
||||
# Reload systemd
|
||||
sudo systemctl daemon-reload
|
||||
```
|
||||
58
devops/ansible/files/zastava-agent.service
Normal file
58
devops/ansible/files/zastava-agent.service
Normal file
@@ -0,0 +1,58 @@
|
||||
[Unit]
|
||||
Description=StellaOps Zastava Agent - Container Runtime Monitor
|
||||
Documentation=https://docs.stellaops.org/zastava/agent/
|
||||
After=network-online.target docker.service containerd.service
|
||||
Wants=network-online.target
|
||||
Requires=docker.service
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
ExecStart=/opt/stellaops/zastava-agent/StellaOps.Zastava.Agent
|
||||
WorkingDirectory=/opt/stellaops/zastava-agent
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
# Environment configuration
|
||||
EnvironmentFile=-/etc/stellaops/zastava-agent.env
|
||||
Environment=DOTNET_ENVIRONMENT=Production
|
||||
Environment=ASPNETCORE_ENVIRONMENT=Production
|
||||
|
||||
# User and permissions
|
||||
User=zastava-agent
|
||||
Group=docker
|
||||
|
||||
# Security hardening
|
||||
NoNewPrivileges=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
PrivateTmp=true
|
||||
PrivateDevices=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectControlGroups=true
|
||||
RestrictRealtime=true
|
||||
RestrictSUIDSGID=true
|
||||
|
||||
# Allow read access to Docker socket
|
||||
ReadWritePaths=/var/run/docker.sock
|
||||
ReadWritePaths=/var/lib/zastava-agent
|
||||
|
||||
# Capabilities
|
||||
CapabilityBoundingSet=
|
||||
AmbientCapabilities=
|
||||
|
||||
# Resource limits
|
||||
LimitNOFILE=65536
|
||||
LimitNPROC=4096
|
||||
MemoryMax=512M
|
||||
|
||||
# Logging
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=zastava-agent
|
||||
|
||||
# Watchdog (5 minute timeout)
|
||||
WatchdogSec=300
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
46
devops/ansible/inventory.yml.sample
Normal file
46
devops/ansible/inventory.yml.sample
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
# Sample Ansible Inventory for Zastava Agent Deployment
|
||||
#
|
||||
# Copy this file to inventory.yml and customize for your environment.
|
||||
# Then run: ansible-playbook -i inventory.yml zastava-agent.yml
|
||||
|
||||
all:
|
||||
children:
|
||||
zastava_agents:
|
||||
hosts:
|
||||
# Add your VM/bare-metal hosts here
|
||||
vm-node-1:
|
||||
ansible_host: 192.168.1.101
|
||||
ansible_user: ubuntu
|
||||
vm-node-2:
|
||||
ansible_host: 192.168.1.102
|
||||
ansible_user: ubuntu
|
||||
# Example with SSH key
|
||||
vm-node-3:
|
||||
ansible_host: 192.168.1.103
|
||||
ansible_user: root
|
||||
ansible_ssh_private_key_file: ~/.ssh/stellaops_key
|
||||
|
||||
vars:
|
||||
# Required: Set these for your environment
|
||||
zastava_tenant: my-tenant
|
||||
scanner_backend_url: https://scanner.example.com
|
||||
|
||||
# Optional: Override node name per host
|
||||
# zastava_node_name: custom-node-name
|
||||
|
||||
# Optional: Change health check port
|
||||
# zastava_health_port: 8080
|
||||
|
||||
# Optional: Custom Docker socket path
|
||||
# docker_socket: /var/run/docker.sock
|
||||
|
||||
# Optional: Set log level (Verbose, Debug, Information, Warning, Error)
|
||||
# zastava_log_level: Information
|
||||
|
||||
# Optional: Allow insecure HTTP (NOT for production)
|
||||
# scanner_backend_insecure: false
|
||||
|
||||
# Optional: Additional environment variables
|
||||
# zastava_extra_env:
|
||||
# CUSTOM_VAR: custom_value
|
||||
40
devops/ansible/templates/zastava-agent.env.j2
Normal file
40
devops/ansible/templates/zastava-agent.env.j2
Normal file
@@ -0,0 +1,40 @@
|
||||
# StellaOps Zastava Agent Configuration
|
||||
# Managed by Ansible - Do not edit manually
|
||||
# Generated: {{ ansible_date_time.iso8601 }}
|
||||
|
||||
# Tenant identifier for multi-tenancy
|
||||
ZASTAVA_TENANT={{ zastava_tenant }}
|
||||
|
||||
# Scanner backend URL
|
||||
ZASTAVA_AGENT__Backend__BaseAddress={{ scanner_backend_url }}
|
||||
|
||||
{% if zastava_node_name is defined %}
|
||||
# Node name override
|
||||
ZASTAVA_NODE_NAME={{ zastava_node_name }}
|
||||
{% endif %}
|
||||
|
||||
# Docker socket endpoint
|
||||
ZASTAVA_AGENT__DockerEndpoint=unix://{{ docker_socket }}
|
||||
|
||||
# Event buffer path
|
||||
ZASTAVA_AGENT__EventBufferPath={{ zastava_data_dir }}/runtime-events
|
||||
|
||||
# Health check port
|
||||
ZASTAVA_AGENT__HealthCheck__Port={{ zastava_health_port }}
|
||||
|
||||
{% if scanner_backend_insecure | default(false) | bool %}
|
||||
# WARNING: Insecure HTTP backend enabled
|
||||
ZASTAVA_AGENT__Backend__AllowInsecureHttp=true
|
||||
{% endif %}
|
||||
|
||||
{% if zastava_log_level is defined %}
|
||||
# Logging level
|
||||
Serilog__MinimumLevel__Default={{ zastava_log_level }}
|
||||
{% endif %}
|
||||
|
||||
{% if zastava_extra_env is defined %}
|
||||
# Additional environment variables
|
||||
{% for key, value in zastava_extra_env.items() %}
|
||||
{{ key }}={{ value }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
232
devops/ansible/zastava-agent.yml
Normal file
232
devops/ansible/zastava-agent.yml
Normal file
@@ -0,0 +1,232 @@
|
||||
---
|
||||
# Ansible Playbook for Zastava Agent VM/Bare-Metal Deployment
|
||||
#
|
||||
# Requirements:
|
||||
# - Target hosts must have Docker installed and running
|
||||
# - Ansible 2.10+ with community.docker collection
|
||||
#
|
||||
# Usage:
|
||||
# ansible-playbook -i inventory.yml zastava-agent.yml \
|
||||
# -e zastava_tenant=my-tenant \
|
||||
# -e scanner_backend_url=https://scanner.internal
|
||||
#
|
||||
# Variables (can be set in inventory or via -e):
|
||||
# zastava_tenant: Tenant identifier (required)
|
||||
# scanner_backend_url: Scanner backend URL (required)
|
||||
# zastava_version: Version to deploy (default: latest)
|
||||
# zastava_node_name: Override node name (default: hostname)
|
||||
# zastava_health_port: Health check port (default: 8080)
|
||||
# docker_socket: Docker socket path (default: /var/run/docker.sock)
|
||||
|
||||
- name: Deploy StellaOps Zastava Agent
|
||||
hosts: zastava_agents
|
||||
become: true
|
||||
|
||||
vars:
|
||||
zastava_version: "{{ zastava_version | default('latest') }}"
|
||||
zastava_install_dir: /opt/stellaops/zastava-agent
|
||||
zastava_config_dir: /etc/stellaops
|
||||
zastava_data_dir: /var/lib/zastava-agent
|
||||
zastava_user: zastava-agent
|
||||
zastava_group: docker
|
||||
zastava_health_port: "{{ zastava_health_port | default(8080) }}"
|
||||
docker_socket: "{{ docker_socket | default('/var/run/docker.sock') }}"
|
||||
download_base_url: "{{ download_base_url | default('https://releases.stellaops.org') }}"
|
||||
|
||||
pre_tasks:
|
||||
- name: Validate required variables
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- zastava_tenant is defined and zastava_tenant | length > 0
|
||||
- scanner_backend_url is defined and scanner_backend_url | length > 0
|
||||
fail_msg: |
|
||||
Required variables not set.
|
||||
Please provide:
|
||||
- zastava_tenant: Your tenant identifier
|
||||
- scanner_backend_url: Scanner backend URL
|
||||
|
||||
- name: Check Docker service is running
|
||||
ansible.builtin.systemd:
|
||||
name: docker
|
||||
state: started
|
||||
check_mode: true
|
||||
register: docker_status
|
||||
|
||||
- name: Fail if Docker is not available
|
||||
ansible.builtin.fail:
|
||||
msg: "Docker service is not running on {{ inventory_hostname }}"
|
||||
when: docker_status.status.ActiveState != 'active'
|
||||
|
||||
tasks:
|
||||
# =========================================================================
|
||||
# User and Directory Setup
|
||||
# =========================================================================
|
||||
|
||||
- name: Create zastava-agent system user
|
||||
ansible.builtin.user:
|
||||
name: "{{ zastava_user }}"
|
||||
comment: StellaOps Zastava Agent
|
||||
system: true
|
||||
shell: /usr/sbin/nologin
|
||||
groups: "{{ zastava_group }}"
|
||||
create_home: false
|
||||
state: present
|
||||
|
||||
- name: Create installation directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ zastava_install_dir }}"
|
||||
state: directory
|
||||
owner: "{{ zastava_user }}"
|
||||
group: "{{ zastava_group }}"
|
||||
mode: '0755'
|
||||
|
||||
- name: Create configuration directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ zastava_config_dir }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0755'
|
||||
|
||||
- name: Create data directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ zastava_data_dir }}"
|
||||
state: directory
|
||||
owner: "{{ zastava_user }}"
|
||||
group: "{{ zastava_group }}"
|
||||
mode: '0750'
|
||||
|
||||
- name: Create event buffer directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ zastava_data_dir }}/runtime-events"
|
||||
state: directory
|
||||
owner: "{{ zastava_user }}"
|
||||
group: "{{ zastava_group }}"
|
||||
mode: '0750'
|
||||
|
||||
# =========================================================================
|
||||
# Download and Install Agent
|
||||
# =========================================================================
|
||||
|
||||
- name: Determine architecture
|
||||
ansible.builtin.set_fact:
|
||||
arch_suffix: "{{ 'x64' if ansible_architecture == 'x86_64' else 'arm64' if ansible_architecture == 'aarch64' else ansible_architecture }}"
|
||||
|
||||
- name: Download Zastava Agent binary
|
||||
ansible.builtin.get_url:
|
||||
url: "{{ download_base_url }}/zastava-agent/{{ zastava_version }}/zastava-agent-linux-{{ arch_suffix }}.tar.gz"
|
||||
dest: /tmp/zastava-agent.tar.gz
|
||||
mode: '0644'
|
||||
register: download_result
|
||||
retries: 3
|
||||
delay: 5
|
||||
|
||||
- name: Extract Zastava Agent
|
||||
ansible.builtin.unarchive:
|
||||
src: /tmp/zastava-agent.tar.gz
|
||||
dest: "{{ zastava_install_dir }}"
|
||||
remote_src: true
|
||||
owner: "{{ zastava_user }}"
|
||||
group: "{{ zastava_group }}"
|
||||
extra_opts:
|
||||
- --strip-components=1
|
||||
notify: Restart zastava-agent
|
||||
|
||||
- name: Make agent binary executable
|
||||
ansible.builtin.file:
|
||||
path: "{{ zastava_install_dir }}/StellaOps.Zastava.Agent"
|
||||
mode: '0755'
|
||||
|
||||
- name: Clean up downloaded archive
|
||||
ansible.builtin.file:
|
||||
path: /tmp/zastava-agent.tar.gz
|
||||
state: absent
|
||||
|
||||
# =========================================================================
|
||||
# Configuration
|
||||
# =========================================================================
|
||||
|
||||
- name: Deploy environment configuration
|
||||
ansible.builtin.template:
|
||||
src: zastava-agent.env.j2
|
||||
dest: "{{ zastava_config_dir }}/zastava-agent.env"
|
||||
owner: root
|
||||
group: "{{ zastava_group }}"
|
||||
mode: '0640'
|
||||
notify: Restart zastava-agent
|
||||
|
||||
# =========================================================================
|
||||
# systemd Service
|
||||
# =========================================================================
|
||||
|
||||
- name: Install systemd service unit
|
||||
ansible.builtin.copy:
|
||||
src: zastava-agent.service
|
||||
dest: /etc/systemd/system/zastava-agent.service
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
notify:
|
||||
- Reload systemd
|
||||
- Restart zastava-agent
|
||||
|
||||
- name: Enable and start zastava-agent service
|
||||
ansible.builtin.systemd:
|
||||
name: zastava-agent
|
||||
state: started
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
|
||||
# =========================================================================
|
||||
# Health Verification
|
||||
# =========================================================================
|
||||
|
||||
- name: Wait for agent health endpoint
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:{{ zastava_health_port }}/healthz"
|
||||
method: GET
|
||||
status_code: 200
|
||||
register: health_result
|
||||
retries: 30
|
||||
delay: 2
|
||||
until: health_result.status == 200
|
||||
|
||||
- name: Display agent status
|
||||
ansible.builtin.debug:
|
||||
msg: "Zastava Agent deployed successfully on {{ inventory_hostname }}"
|
||||
|
||||
handlers:
|
||||
- name: Reload systemd
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
|
||||
- name: Restart zastava-agent
|
||||
ansible.builtin.systemd:
|
||||
name: zastava-agent
|
||||
state: restarted
|
||||
|
||||
# =============================================================================
|
||||
# Post-deployment verification play
|
||||
# =============================================================================
|
||||
- name: Verify Zastava Agent Deployment
|
||||
hosts: zastava_agents
|
||||
become: false
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
- name: Check agent readiness
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:{{ zastava_health_port | default(8080) }}/readyz"
|
||||
method: GET
|
||||
return_content: true
|
||||
register: ready_check
|
||||
|
||||
- name: Display deployment summary
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
Zastava Agent Deployment Summary:
|
||||
- Host: {{ inventory_hostname }}
|
||||
- Status: {{ 'Ready' if ready_check.status == 200 else 'Not Ready' }}
|
||||
- Health Endpoint: http://localhost:{{ zastava_health_port | default(8080) }}/healthz
|
||||
- Tenant: {{ zastava_tenant }}
|
||||
- Backend: {{ scanner_backend_url }}
|
||||
@@ -0,0 +1,474 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TestRun id="f3a0021b-dfb3-4082-af95-f1eafac6d6e5" name="@DESKTOP-7GHGC2M 2025-11-25 03:06:57" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
|
||||
<Times creation="2025-11-25T03:06:57.6881410+00:00" queuing="2025-11-25T03:06:57.6881411+00:00" start="2025-11-25T03:06:19.0356492+00:00" finish="2025-11-25T03:06:57.6979352+00:00" />
|
||||
<TestSettings name="default" id="67ad58d5-5c7e-42c3-a9e6-344f5eac3e53">
|
||||
<Deployment runDeploymentRoot="_DESKTOP-7GHGC2M_2025-11-25_03_06_57" />
|
||||
</TestSettings>
|
||||
<Results>
|
||||
<UnitTestResult executionId="d3dec3a6-6647-4d62-a7d5-f372009ed25b" testId="fbedf19a-bc7b-9a2d-979a-ba574f7a6f23" testName="StellaOps.Concelier.WebService.Tests.WebServiceEndpointsTests.HealthAndReadyEndpointsRespond" computerName="DESKTOP-7GHGC2M" duration="00:00:00.4031915" startTime="2025-11-25T03:06:57.5011498+00:00" endTime="2025-11-25T03:06:57.5011813+00:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="d3dec3a6-6647-4d62-a7d5-f372009ed25b" />
|
||||
</Results>
|
||||
<TestDefinitions>
|
||||
<UnitTest name="StellaOps.Concelier.WebService.Tests.WebServiceEndpointsTests.HealthAndReadyEndpointsRespond" storage="/mnt/e/dev/git.stella-ops.org/src/concelier/__tests/stellaops.concelier.webservice.tests/bin/debug/net10.0/stellaops.concelier.webservice.tests.dll" id="fbedf19a-bc7b-9a2d-979a-ba574f7a6f23">
|
||||
<Execution id="d3dec3a6-6647-4d62-a7d5-f372009ed25b" />
|
||||
<TestMethod codeBase="/mnt/e/dev/git.stella-ops.org/src/Concelier/__Tests/StellaOps.Concelier.WebService.Tests/bin/Debug/net10.0/StellaOps.Concelier.WebService.Tests.dll" adapterTypeName="executor://xunit/VsTestRunner2/netcoreapp" className="StellaOps.Concelier.WebService.Tests.WebServiceEndpointsTests" name="HealthAndReadyEndpointsRespond" />
|
||||
</UnitTest>
|
||||
</TestDefinitions>
|
||||
<TestEntries>
|
||||
<TestEntry testId="fbedf19a-bc7b-9a2d-979a-ba574f7a6f23" executionId="d3dec3a6-6647-4d62-a7d5-f372009ed25b" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
|
||||
</TestEntries>
|
||||
<TestLists>
|
||||
<TestList name="Results Not in a List" id="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
|
||||
<TestList name="All Loaded Results" id="19431567-8539-422a-85d7-44ee4e166bda" />
|
||||
</TestLists>
|
||||
<ResultSummary outcome="Completed">
|
||||
<Counters total="1" executed="1" passed="1" failed="0" error="0" timeout="0" aborted="0" inconclusive="0" passedButRunAborted="0" notRunnable="0" notExecuted="0" disconnected="0" warning="0" completed="0" inProgress="0" pending="0" />
|
||||
<Output>
|
||||
<StdOut>[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0-rc.2.25502.107)
|
||||
[xUnit.net 00:00:00.26] Discovering: StellaOps.Concelier.WebService.Tests
|
||||
[xUnit.net 00:00:00.33] Discovered: StellaOps.Concelier.WebService.Tests
|
||||
[xUnit.net 00:00:00.34] Starting: StellaOps.Concelier.WebService.Tests
|
||||
{"t":{"$date":"2025-11-25T03:06:53.170+00:00"},"s":"I", "c":"CONTROL", "id":23285, "ctx":"main","msg":"Automatically disabling TLS 1.0, to force-enable TLS 1.0 specify --sslDisabledProtocols 'none'"}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.171+00:00"},"s":"W", "c":"ASIO", "id":22601, "ctx":"main","msg":"No TransportLayer configured during NetworkInterface startup"}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.171+00:00"},"s":"I", "c":"NETWORK", "id":4648601, "ctx":"main","msg":"Implicit TCP FastOpen unavailable. If TCP FastOpen is required, set tcpFastOpenServer, tcpFastOpenClient, and tcpFastOpenQueueSize."}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.171+00:00"},"s":"W", "c":"ASIO", "id":22601, "ctx":"main","msg":"No TransportLayer configured during NetworkInterface startup"}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.172+00:00"},"s":"I", "c":"STORAGE", "id":4615611, "ctx":"initandlisten","msg":"MongoDB starting","attr":{"pid":138154,"port":33929,"dbPath":"/tmp/yifc3x13.bsnecd0ff0e2d3d45ff96e2_33929","architecture":"64-bit","host":"DESKTOP-7GHGC2M"}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.172+00:00"},"s":"I", "c":"CONTROL", "id":23403, "ctx":"initandlisten","msg":"Build Info","attr":{"buildInfo":{"version":"4.4.4","gitVersion":"8db30a63db1a9d84bdcad0c83369623f708e0397","openSSLVersion":"OpenSSL 1.1.1f 31 Mar 2020","modules":[],"allocator":"tcmalloc","environment":{"distmod":"ubuntu2004","distarch":"x86_64","target_arch":"x86_64"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.172+00:00"},"s":"I", "c":"CONTROL", "id":51765, "ctx":"initandlisten","msg":"Operating System","attr":{"os":{"name":"Ubuntu","version":"24.04"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.172+00:00"},"s":"I", "c":"CONTROL", "id":21951, "ctx":"initandlisten","msg":"Options set by command line","attr":{"options":{"net":{"bindIp":"127.0.0.1","port":33929},"replication":{"replSet":"singleNodeReplSet"},"storage":{"dbPath":"/tmp/yifc3x13.bsnecd0ff0e2d3d45ff96e2_33929"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.173+00:00"},"s":"I", "c":"STORAGE", "id":22297, "ctx":"initandlisten","msg":"Using the XFS filesystem is strongly recommended with the WiredTiger storage engine. See http://dochub.mongodb.org/core/prodnotes-filesystem","tags":["startupWarnings"]}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.174+00:00"},"s":"I", "c":"STORAGE", "id":22315, "ctx":"initandlisten","msg":"Opening WiredTiger","attr":{"config":"create,cache_size=7485M,session_max=33000,eviction=(threads_min=4,threads_max=4),config_base=false,statistics=(fast),log=(enabled=true,archive=true,path=journal,compressor=snappy),file_manager=(close_idle_time=100000,close_scan_interval=10,close_handle_minimum=250),statistics_log=(wait=0),verbose=[recovery_progress,checkpoint_progress,compact_progress],"}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.622+00:00"},"s":"I", "c":"STORAGE", "id":22430, "ctx":"initandlisten","msg":"WiredTiger message","attr":{"message":"[1764040013:622123][138154:0x72dd8d1c4cc0], txn-recover: [WT_VERB_RECOVERY | WT_VERB_RECOVERY_PROGRESS] Set global recovery timestamp: (0, 0)"}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.622+00:00"},"s":"I", "c":"STORAGE", "id":22430, "ctx":"initandlisten","msg":"WiredTiger message","attr":{"message":"[1764040013:622190][138154:0x72dd8d1c4cc0], txn-recover: [WT_VERB_RECOVERY | WT_VERB_RECOVERY_PROGRESS] Set global oldest timestamp: (0, 0)"}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.635+00:00"},"s":"I", "c":"STORAGE", "id":4795906, "ctx":"initandlisten","msg":"WiredTiger opened","attr":{"durationMillis":461}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.635+00:00"},"s":"I", "c":"RECOVERY", "id":23987, "ctx":"initandlisten","msg":"WiredTiger recoveryTimestamp","attr":{"recoveryTimestamp":{"$timestamp":{"t":0,"i":0}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.667+00:00"},"s":"I", "c":"STORAGE", "id":4366408, "ctx":"initandlisten","msg":"No table logging settings modifications are required for existing WiredTiger tables","attr":{"loggingEnabled":false}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.668+00:00"},"s":"I", "c":"STORAGE", "id":22262, "ctx":"initandlisten","msg":"Timestamp monitor starting"}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.676+00:00"},"s":"W", "c":"CONTROL", "id":22120, "ctx":"initandlisten","msg":"Access control is not enabled for the database. Read and write access to data and configuration is unrestricted","tags":["startupWarnings"]}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.677+00:00"},"s":"I", "c":"STORAGE", "id":20536, "ctx":"initandlisten","msg":"Flow Control is enabled on this deployment"}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.679+00:00"},"s":"I", "c":"SHARDING", "id":20997, "ctx":"initandlisten","msg":"Refreshed RWC defaults","attr":{"newDefaults":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.679+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"initandlisten","msg":"createCollection","attr":{"namespace":"local.startup_log","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"5f608eed-817b-4ac1-94e3-ae0e0a954ec5"}},"options":{"capped":true,"size":10485760}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.697+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"initandlisten","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"local.startup_log","index":"_id_","commitTimestamp":{"$timestamp":{"t":0,"i":0}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.697+00:00"},"s":"I", "c":"FTDC", "id":20625, "ctx":"initandlisten","msg":"Initializing full-time diagnostic data capture","attr":{"dataDirectory":"/tmp/yifc3x13.bsnecd0ff0e2d3d45ff96e2_33929/diagnostic.data"}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.699+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"initandlisten","msg":"createCollection","attr":{"namespace":"local.replset.oplogTruncateAfterPoint","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"763ae47e-5634-4a14-9ef6-4ffd6dc93918"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.720+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"initandlisten","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"local.replset.oplogTruncateAfterPoint","index":"_id_","commitTimestamp":{"$timestamp":{"t":0,"i":0}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.720+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"initandlisten","msg":"createCollection","attr":{"namespace":"local.replset.minvalid","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"54bed8e9-a7bd-4897-8c05-ad4fa62f77c5"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.740+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"initandlisten","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"local.replset.minvalid","index":"_id_","commitTimestamp":{"$timestamp":{"t":0,"i":0}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.740+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"initandlisten","msg":"createCollection","attr":{"namespace":"local.replset.election","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"97e32968-ba25-4803-bcca-c4008661ee27"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.759+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"initandlisten","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"local.replset.election","index":"_id_","commitTimestamp":{"$timestamp":{"t":0,"i":0}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.760+00:00"},"s":"I", "c":"REPL", "id":21311, "ctx":"initandlisten","msg":"Did not find local initialized voted for document at startup"}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.760+00:00"},"s":"I", "c":"REPL", "id":21312, "ctx":"initandlisten","msg":"Did not find local Rollback ID document at startup. Creating one"}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.760+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"initandlisten","msg":"createCollection","attr":{"namespace":"local.system.rollback.id","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"c9e78c6d-5f57-428c-b6d4-05340e2fef65"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.781+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"initandlisten","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"local.system.rollback.id","index":"_id_","commitTimestamp":{"$timestamp":{"t":0,"i":0}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.781+00:00"},"s":"I", "c":"REPL", "id":21531, "ctx":"initandlisten","msg":"Initialized the rollback ID","attr":{"rbid":1}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.781+00:00"},"s":"I", "c":"REPL", "id":21313, "ctx":"initandlisten","msg":"Did not find local replica set configuration document at startup","attr":{"error":{"code":47,"codeName":"NoMatchingDocument","errmsg":"Did not find replica set configuration document in local.system.replset"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.782+00:00"},"s":"I", "c":"CONTROL", "id":20714, "ctx":"LogicalSessionCacheRefresh","msg":"Failed to refresh session cache, will try again at the next refresh interval","attr":{"error":"NotYetInitialized: Replication has not yet been configured"}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.783+00:00"},"s":"I", "c":"CONTROL", "id":20712, "ctx":"LogicalSessionCacheReap","msg":"Sessions collection is not set up; waiting until next sessions reap interval","attr":{"error":"NamespaceNotFound: config.system.sessions does not exist"}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.783+00:00"},"s":"I", "c":"REPL", "id":40440, "ctx":"initandlisten","msg":"Starting the TopologyVersionObserver"}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.783+00:00"},"s":"I", "c":"REPL", "id":40445, "ctx":"TopologyVersionObserver","msg":"Started TopologyVersionObserver"}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.784+00:00"},"s":"I", "c":"NETWORK", "id":23015, "ctx":"listener","msg":"Listening on","attr":{"address":"/tmp/mongodb-33929.sock"}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.784+00:00"},"s":"I", "c":"NETWORK", "id":23015, "ctx":"listener","msg":"Listening on","attr":{"address":"127.0.0.1"}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.784+00:00"},"s":"I", "c":"NETWORK", "id":23016, "ctx":"listener","msg":"Waiting for connections","attr":{"port":33929,"ssl":"off"}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.796+00:00"},"s":"I", "c":"NETWORK", "id":22943, "ctx":"listener","msg":"Connection accepted","attr":{"remote":"127.0.0.1:47046","connectionId":1,"connectionCount":1}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.820+00:00"},"s":"I", "c":"NETWORK", "id":51800, "ctx":"conn1","msg":"client metadata","attr":{"remote":"127.0.0.1:47046","client":"conn1","doc":{"driver":{"name":"mongo-csharp-driver","version":"3.5.0"},"os":{"type":"Linux","name":"Ubuntu 24.04.3 LTS","architecture":"x86_64","version":"24.04.3"},"platform":".NET 10.0.0-rc.2.25502.107"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.852+00:00"},"s":"I", "c":"NETWORK", "id":22943, "ctx":"listener","msg":"Connection accepted","attr":{"remote":"127.0.0.1:47050","connectionId":2,"connectionCount":2}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.854+00:00"},"s":"I", "c":"NETWORK", "id":51800, "ctx":"conn2","msg":"client metadata","attr":{"remote":"127.0.0.1:47050","client":"conn2","doc":{"driver":{"name":"mongo-csharp-driver","version":"3.5.0"},"os":{"type":"Linux","name":"Ubuntu 24.04.3 LTS","architecture":"x86_64","version":"24.04.3"},"platform":".NET 10.0.0-rc.2.25502.107"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.859+00:00"},"s":"I", "c":"NETWORK", "id":22943, "ctx":"listener","msg":"Connection accepted","attr":{"remote":"127.0.0.1:47052","connectionId":3,"connectionCount":3}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.860+00:00"},"s":"I", "c":"NETWORK", "id":51800, "ctx":"conn3","msg":"client metadata","attr":{"remote":"127.0.0.1:47052","client":"conn3","doc":{"driver":{"name":"mongo-csharp-driver","version":"3.5.0"},"os":{"type":"Linux","name":"Ubuntu 24.04.3 LTS","architecture":"x86_64","version":"24.04.3"},"platform":".NET 10.0.0-rc.2.25502.107"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.872+00:00"},"s":"I", "c":"REPL", "id":21356, "ctx":"conn3","msg":"replSetInitiate admin command received from client"}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.872+00:00"},"s":"I", "c":"REPL", "id":21357, "ctx":"conn3","msg":"replSetInitiate config object parses ok","attr":{"numMembers":1}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.872+00:00"},"s":"I", "c":"REPL", "id":21251, "ctx":"conn3","msg":"Creating replication oplog","attr":{"oplogSizeMB":48118}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.872+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"local.oplog.rs","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"26641ba6-7282-4c09-a7b5-c06683c09d25"}},"options":{"capped":true,"size":50456355840.0,"autoIndexId":false}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.881+00:00"},"s":"I", "c":"STORAGE", "id":22383, "ctx":"conn3","msg":"The size storer reports that the oplog contains","attr":{"numRecords":0,"dataSize":0}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.881+00:00"},"s":"I", "c":"STORAGE", "id":22382, "ctx":"conn3","msg":"WiredTiger record store oplog processing finished","attr":{"durationMillis":0}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.921+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"local.system.replset","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"e329ace1-6110-413a-a7f9-c929c43b7823"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.941+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"local.system.replset","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040013,"i":1}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.942+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"admin.system.version","uuidDisposition":"provided","uuid":{"uuid":{"$uuid":"1515c214-38af-4280-bd1e-e79281395c7e"}},"options":{"uuid":{"$uuid":"1515c214-38af-4280-bd1e-e79281395c7e"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.959+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"admin.system.version","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040013,"i":1}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.959+00:00"},"s":"I", "c":"COMMAND", "id":20459, "ctx":"conn3","msg":"Setting featureCompatibilityVersion","attr":{"newVersion":"4.4"}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.959+00:00"},"s":"I", "c":"NETWORK", "id":22991, "ctx":"conn3","msg":"Skip closing connection for connection","attr":{"connectionId":3}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.959+00:00"},"s":"I", "c":"NETWORK", "id":22991, "ctx":"conn3","msg":"Skip closing connection for connection","attr":{"connectionId":2}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.959+00:00"},"s":"I", "c":"NETWORK", "id":22991, "ctx":"conn3","msg":"Skip closing connection for connection","attr":{"connectionId":1}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.959+00:00"},"s":"I", "c":"REPL", "id":21392, "ctx":"conn3","msg":"New replica set config in use","attr":{"config":{"_id":"singleNodeReplSet","version":1,"term":0,"protocolVersion":1,"writeConcernMajorityJournalDefault":true,"members":[{"_id":0,"host":"127.0.0.1:33929","arbiterOnly":false,"buildIndexes":true,"hidden":false,"priority":1.0,"tags":{},"slaveDelay":0,"votes":1}],"settings":{"chainingAllowed":true,"heartbeatIntervalMillis":2000,"heartbeatTimeoutSecs":10,"electionTimeoutMillis":10000,"catchUpTimeoutMillis":-1,"catchUpTakeoverDelayMillis":30000,"getLastErrorModes":{},"getLastErrorDefaults":{"w":1,"wtimeout":0},"replicaSetId":{"$oid":"69251d4d4fa9b5bd940f91b6"}}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.959+00:00"},"s":"I", "c":"REPL", "id":21393, "ctx":"conn3","msg":"Found self in config","attr":{"hostAndPort":"127.0.0.1:33929"}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.959+00:00"},"s":"I", "c":"REPL", "id":21358, "ctx":"conn3","msg":"Replica set state transition","attr":{"newState":"STARTUP2","oldState":"STARTUP"}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.959+00:00"},"s":"I", "c":"REPL", "id":21306, "ctx":"conn3","msg":"Starting replication storage threads"}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.963+00:00"},"s":"I", "c":"REPL", "id":21358, "ctx":"conn3","msg":"Replica set state transition","attr":{"newState":"RECOVERING","oldState":"STARTUP2"}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.963+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"local.replset.initialSyncId","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"2f0157f6-a696-485a-90cd-25ebdc98434e"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.981+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"local.replset.initialSyncId","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040013,"i":1}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.981+00:00"},"s":"I", "c":"REPL", "id":21299, "ctx":"conn3","msg":"Starting replication fetcher thread"}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.981+00:00"},"s":"I", "c":"REPL", "id":21300, "ctx":"conn3","msg":"Starting replication applier thread"}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.981+00:00"},"s":"I", "c":"REPL", "id":21301, "ctx":"conn3","msg":"Starting replication reporter thread"}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.981+00:00"},"s":"I", "c":"REPL", "id":21224, "ctx":"OplogApplier-0","msg":"Starting oplog application"}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.981+00:00"},"s":"I", "c":"COMMAND", "id":51803, "ctx":"conn3","msg":"Slow query","attr":{"type":"command","ns":"local.system.replset","command":{"replSetInitiate":{"_id":"singleNodeReplSet","members":[{"_id":0,"host":"127.0.0.1:33929"}]},"$db":"admin","lsid":{"id":{"$uuid":"e2f41f2f-e77f-4af9-81e0-32d8592d6a54"}}},"numYields":0,"reslen":163,"locks":{"ParallelBatchWriterMode":{"acquireCount":{"r":18}},"ReplicationStateTransition":{"acquireCount":{"w":19}},"Global":{"acquireCount":{"r":11,"w":6,"W":2}},"Database":{"acquireCount":{"r":10,"w":4,"W":2}},"Collection":{"acquireCount":{"r":3,"w":5}},"Mutex":{"acquireCount":{"r":17}},"oplog":{"acquireCount":{"w":1}}},"flowControl":{"acquireCount":5,"timeAcquiringMicros":5},"storage":{},"protocol":"op_msg","durationMillis":109}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.982+00:00"},"s":"I", "c":"REPL", "id":21358, "ctx":"OplogApplier-0","msg":"Replica set state transition","attr":{"newState":"SECONDARY","oldState":"RECOVERING"}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.982+00:00"},"s":"I", "c":"ELECTION", "id":4615652, "ctx":"OplogApplier-0","msg":"Starting an election, since we've seen no PRIMARY in election timeout period","attr":{"electionTimeoutPeriodMillis":10000}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.982+00:00"},"s":"I", "c":"ELECTION", "id":21438, "ctx":"OplogApplier-0","msg":"Conducting a dry run election to see if we could be elected","attr":{"currentTerm":0}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.982+00:00"},"s":"I", "c":"ELECTION", "id":21444, "ctx":"ReplCoord-0","msg":"Dry election run succeeded, running for election","attr":{"newTerm":1}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.984+00:00"},"s":"I", "c":"ELECTION", "id":21450, "ctx":"ReplCoord-1","msg":"Election succeeded, assuming primary role","attr":{"term":1}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.984+00:00"},"s":"I", "c":"REPL", "id":21358, "ctx":"ReplCoord-1","msg":"Replica set state transition","attr":{"newState":"PRIMARY","oldState":"SECONDARY"}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.984+00:00"},"s":"I", "c":"REPL", "id":21106, "ctx":"ReplCoord-1","msg":"Resetting sync source to empty","attr":{"previousSyncSource":":27017"}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.984+00:00"},"s":"I", "c":"REPL", "id":21359, "ctx":"ReplCoord-1","msg":"Entering primary catch-up mode"}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.984+00:00"},"s":"I", "c":"REPL", "id":21363, "ctx":"ReplCoord-1","msg":"Exited primary catch-up mode"}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.984+00:00"},"s":"I", "c":"REPL", "id":21107, "ctx":"ReplCoord-1","msg":"Stopping replication producer"}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.984+00:00"},"s":"I", "c":"REPL", "id":21239, "ctx":"ReplBatcher","msg":"Oplog buffer has been drained","attr":{"term":1}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.984+00:00"},"s":"I", "c":"REPL", "id":21343, "ctx":"RstlKillOpThread","msg":"Starting to kill user operations"}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.984+00:00"},"s":"I", "c":"REPL", "id":21344, "ctx":"RstlKillOpThread","msg":"Stopped killing user operations"}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.985+00:00"},"s":"I", "c":"REPL", "id":21340, "ctx":"RstlKillOpThread","msg":"State transition ops metrics","attr":{"metrics":{"lastStateTransition":"stepUp","userOpsKilled":0,"userOpsRunning":1}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.985+00:00"},"s":"I", "c":"REPL", "id":4508103, "ctx":"OplogApplier-0","msg":"Increment the config term via reconfig"}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.985+00:00"},"s":"I", "c":"REPL", "id":21353, "ctx":"OplogApplier-0","msg":"replSetReconfig config object parses ok","attr":{"numMembers":1}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.985+00:00"},"s":"I", "c":"REPL", "id":51814, "ctx":"OplogApplier-0","msg":"Persisting new config to disk"}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.986+00:00"},"s":"I", "c":"REPL", "id":21392, "ctx":"OplogApplier-0","msg":"New replica set config in use","attr":{"config":{"_id":"singleNodeReplSet","version":1,"term":1,"protocolVersion":1,"writeConcernMajorityJournalDefault":true,"members":[{"_id":0,"host":"127.0.0.1:33929","arbiterOnly":false,"buildIndexes":true,"hidden":false,"priority":1.0,"tags":{},"slaveDelay":0,"votes":1}],"settings":{"chainingAllowed":true,"heartbeatIntervalMillis":2000,"heartbeatTimeoutSecs":10,"electionTimeoutMillis":10000,"catchUpTimeoutMillis":-1,"catchUpTakeoverDelayMillis":30000,"getLastErrorModes":{},"getLastErrorDefaults":{"w":1,"wtimeout":0},"replicaSetId":{"$oid":"69251d4d4fa9b5bd940f91b6"}}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.986+00:00"},"s":"I", "c":"REPL", "id":21393, "ctx":"OplogApplier-0","msg":"Found self in config","attr":{"hostAndPort":"127.0.0.1:33929"}}
|
||||
{"t":{"$date":"2025-11-25T03:06:53.986+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"OplogApplier-0","msg":"createCollection","attr":{"namespace":"config.transactions","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"7b6e3a69-23e4-40fc-8365-b5c595eea4b1"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:54.003+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"OplogApplier-0","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"config.transactions","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040013,"i":3}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:54.003+00:00"},"s":"I", "c":"STORAGE", "id":20657, "ctx":"OplogApplier-0","msg":"IndexBuildsCoordinator::onStepUp - this node is stepping up to primary"}
|
||||
{"t":{"$date":"2025-11-25T03:06:54.004+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"OplogApplier-0","msg":"createCollection","attr":{"namespace":"config.system.indexBuilds","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"a78b2f2c-d49d-40ec-b777-5c4f678f8ea2"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:54.019+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"OplogApplier-0","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"config.system.indexBuilds","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040014,"i":2}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:54.019+00:00"},"s":"I", "c":"REPL", "id":21331, "ctx":"OplogApplier-0","msg":"Transition to primary complete; database writes are now permitted"}
|
||||
{"t":{"$date":"2025-11-25T03:06:54.020+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"monitoring-keys-for-HMAC","msg":"createCollection","attr":{"namespace":"admin.system.keys","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"5ac7de93-626f-4635-aad0-423907ebaaae"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:54.036+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"monitoring-keys-for-HMAC","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"admin.system.keys","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040014,"i":3}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:54.038+00:00"},"s":"I", "c":"STORAGE", "id":22310, "ctx":"WTJournalFlusher","msg":"Triggering the first stable checkpoint","attr":{"initialData":{"$timestamp":{"t":1764040013,"i":1}},"prevStable":{"$timestamp":{"t":0,"i":0}},"currStable":{"$timestamp":{"t":1764040014,"i":4}}}}
|
||||
warn: StellaOps.Concelier.WebService[0]
|
||||
Authority enabled: False, test signing secret configured: True
|
||||
warn: StellaOps.Concelier.WebService[0]
|
||||
Legacy merge module disabled via concelier:features:noMergeEnabled; Link-Not-Merge mode active.
|
||||
{"t":{"$date":"2025-11-25T03:06:55.284+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"concelier.source","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"b5dcc880-19fc-4c9b-a878-ac99d5f77246"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.308+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.source","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040015,"i":1}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.MongoBootstrapper[0]
|
||||
Created Mongo collection source
|
||||
{"t":{"$date":"2025-11-25T03:06:55.316+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"concelier.source_state","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"9475ed88-bc52-4c6d-abcb-5dfaf2d5cf5b"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.336+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.source_state","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040015,"i":2}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.MongoBootstrapper[0]
|
||||
Created Mongo collection source_state
|
||||
{"t":{"$date":"2025-11-25T03:06:55.339+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"concelier.document","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"ad2a5b15-8e65-4ed1-9efa-d0fd1e643131"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.358+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.document","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040015,"i":3}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.MongoBootstrapper[0]
|
||||
Created Mongo collection document
|
||||
{"t":{"$date":"2025-11-25T03:06:55.361+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"concelier.dto","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"81a72f1b-58d0-4a25-a0bc-0dd9362247f8"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.377+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.dto","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040015,"i":4}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.MongoBootstrapper[0]
|
||||
Created Mongo collection dto
|
||||
{"t":{"$date":"2025-11-25T03:06:55.380+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"concelier.advisory","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"c2e4124c-bf80-4e3c-9272-cea8f40106f5"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.397+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.advisory","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040015,"i":5}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.MongoBootstrapper[0]
|
||||
Created Mongo collection advisory
|
||||
{"t":{"$date":"2025-11-25T03:06:55.400+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"concelier.advisory_raw","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"70542ec2-832b-4f93-8c96-4ca814f1fbbc"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.416+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.advisory_raw","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040015,"i":6}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.MongoBootstrapper[0]
|
||||
Created Mongo collection advisory_raw
|
||||
{"t":{"$date":"2025-11-25T03:06:55.419+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"concelier.alias","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"6a6a3cc5-2ba2-4756-bf3f-197fd1a306a0"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.435+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.alias","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040015,"i":7}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.MongoBootstrapper[0]
|
||||
Created Mongo collection alias
|
||||
{"t":{"$date":"2025-11-25T03:06:55.438+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"concelier.affected","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"ef930a9b-1097-41f9-9d77-2659520d64dc"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.456+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.affected","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040015,"i":8}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.MongoBootstrapper[0]
|
||||
Created Mongo collection affected
|
||||
{"t":{"$date":"2025-11-25T03:06:55.460+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"concelier.reference","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"24d0213b-0677-42fa-b7ae-b0a19b36317d"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.495+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.reference","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040015,"i":9}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.MongoBootstrapper[0]
|
||||
Created Mongo collection reference
|
||||
{"t":{"$date":"2025-11-25T03:06:55.499+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"concelier.kev_flag","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"3155caef-fd8b-4512-8480-f18fea9f8ae9"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.520+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.kev_flag","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040015,"i":10}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.MongoBootstrapper[0]
|
||||
Created Mongo collection kev_flag
|
||||
{"t":{"$date":"2025-11-25T03:06:55.524+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"concelier.ru_flags","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"4c64a0cb-1b22-4055-8cf9-2ddaf8b2eecc"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.541+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.ru_flags","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040015,"i":11}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.MongoBootstrapper[0]
|
||||
Created Mongo collection ru_flags
|
||||
{"t":{"$date":"2025-11-25T03:06:55.544+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"concelier.jp_flags","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"39e4df97-ce8e-4ae2-9996-eae3fb682e43"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.562+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.jp_flags","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040015,"i":12}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.MongoBootstrapper[0]
|
||||
Created Mongo collection jp_flags
|
||||
{"t":{"$date":"2025-11-25T03:06:55.565+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"concelier.psirt_flags","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"d61fab06-e185-4905-a581-78d6188f9cbf"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.597+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.psirt_flags","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040015,"i":13}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.MongoBootstrapper[0]
|
||||
Created Mongo collection psirt_flags
|
||||
{"t":{"$date":"2025-11-25T03:06:55.600+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"concelier.merge_event","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"21f05a29-c17f-4fae-af85-30ede0275435"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.621+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.merge_event","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040015,"i":14}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.MongoBootstrapper[0]
|
||||
Created Mongo collection merge_event
|
||||
{"t":{"$date":"2025-11-25T03:06:55.624+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"concelier.export_state","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"1d816e12-6eb0-40fa-87ae-8bac12a31e53"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.642+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.export_state","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040015,"i":15}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.MongoBootstrapper[0]
|
||||
Created Mongo collection export_state
|
||||
{"t":{"$date":"2025-11-25T03:06:55.645+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"concelier.source_change_history","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"0c0938b6-7eb1-4e92-a8a8-5ed971581ddc"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.662+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.source_change_history","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040015,"i":16}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.MongoBootstrapper[0]
|
||||
Created Mongo collection source_change_history
|
||||
{"t":{"$date":"2025-11-25T03:06:55.665+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"concelier.advisory_statements","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"46b5cd3a-fd22-47d2-81cc-2c756d9cfe62"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.682+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.advisory_statements","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040015,"i":17}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.MongoBootstrapper[0]
|
||||
Created Mongo collection advisory_statements
|
||||
{"t":{"$date":"2025-11-25T03:06:55.685+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"concelier.advisory_conflicts","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"e830a702-eb38-4e79-bd71-139b63066228"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.702+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.advisory_conflicts","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040015,"i":18}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.MongoBootstrapper[0]
|
||||
Created Mongo collection advisory_conflicts
|
||||
{"t":{"$date":"2025-11-25T03:06:55.705+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"concelier.advisory_observations","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"2d30c6a9-a970-4507-9548-c93174011df9"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.730+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.advisory_observations","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040015,"i":19}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.MongoBootstrapper[0]
|
||||
Created Mongo collection advisory_observations
|
||||
{"t":{"$date":"2025-11-25T03:06:55.733+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"concelier.locks","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"c8dc3f6d-0481-4693-ad61-36b23257b47f"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.752+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.locks","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040015,"i":20}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.MongoBootstrapper[0]
|
||||
Created Mongo collection locks
|
||||
{"t":{"$date":"2025-11-25T03:06:55.755+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"concelier.jobs","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"b46075e8-3e6f-4a66-913f-60021219351a"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.773+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.jobs","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040015,"i":21}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.MongoBootstrapper[0]
|
||||
Created Mongo collection jobs
|
||||
{"t":{"$date":"2025-11-25T03:06:55.776+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn3","msg":"createCollection","attr":{"namespace":"concelier.schema_migrations","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"41eb8ab9-7155-4f1f-929d-bf08fe8d877e"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.798+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.schema_migrations","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040015,"i":22}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.MongoBootstrapper[0]
|
||||
Created Mongo collection schema_migrations
|
||||
{"t":{"$date":"2025-11-25T03:06:55.823+00:00"},"s":"I", "c":"INDEX", "id":20438, "ctx":"conn3","msg":"Index build: registering","attr":{"buildUUID":{"uuid":{"$uuid":"5c6b2846-9d1d-46de-ac5f-b2f85a6d097c"}},"namespace":"concelier.locks","collectionUUID":{"uuid":{"$uuid":"c8dc3f6d-0481-4693-ad61-36b23257b47f"}},"indexes":1,"firstIndex":{"name":"ttl_at_ttl"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.831+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.locks","index":"ttl_at_ttl","commitTimestamp":{"$timestamp":{"t":1764040015,"i":23}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.831+00:00"},"s":"I", "c":"INDEX", "id":20440, "ctx":"conn3","msg":"Index build: waiting for index build to complete","attr":{"buildUUID":{"uuid":{"$uuid":"5c6b2846-9d1d-46de-ac5f-b2f85a6d097c"}},"deadline":{"$date":{"$numberLong":"9223372036854775807"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.831+00:00"},"s":"I", "c":"INDEX", "id":20447, "ctx":"conn3","msg":"Index build: completed","attr":{"buildUUID":{"uuid":{"$uuid":"5c6b2846-9d1d-46de-ac5f-b2f85a6d097c"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.835+00:00"},"s":"I", "c":"NETWORK", "id":22943, "ctx":"listener","msg":"Connection accepted","attr":{"remote":"127.0.0.1:47428","connectionId":4,"connectionCount":4}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.841+00:00"},"s":"I", "c":"NETWORK", "id":51800, "ctx":"conn4","msg":"client metadata","attr":{"remote":"127.0.0.1:47428","client":"conn4","doc":{"driver":{"name":"mongo-csharp-driver","version":"3.5.0"},"os":{"type":"Linux","name":"Ubuntu 24.04.3 LTS","architecture":"x86_64","version":"24.04.3"},"platform":".NET 10.0.0-rc.2.25502.107"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.849+00:00"},"s":"I", "c":"INDEX", "id":20438, "ctx":"conn4","msg":"Index build: registering","attr":{"buildUUID":{"uuid":{"$uuid":"1146b67d-4236-4bc9-bae4-3fa891517889"}},"namespace":"concelier.jobs","collectionUUID":{"uuid":{"$uuid":"b46075e8-3e6f-4a66-913f-60021219351a"}},"indexes":3,"firstIndex":{"name":"jobs_createdAt_desc"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.858+00:00"},"s":"I", "c":"NETWORK", "id":22943, "ctx":"listener","msg":"Connection accepted","attr":{"remote":"127.0.0.1:47432","connectionId":5,"connectionCount":5}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.858+00:00"},"s":"I", "c":"INDEX", "id":20438, "ctx":"conn3","msg":"Index build: registering","attr":{"buildUUID":{"uuid":{"$uuid":"b267ade3-39a8-4744-8ffe-e091e3a60a76"}},"namespace":"concelier.advisory","collectionUUID":{"uuid":{"$uuid":"c2e4124c-bf80-4e3c-9272-cea8f40106f5"}},"indexes":5,"firstIndex":{"name":"advisory_key_unique"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.859+00:00"},"s":"I", "c":"NETWORK", "id":51800, "ctx":"conn5","msg":"client metadata","attr":{"remote":"127.0.0.1:47432","client":"conn5","doc":{"driver":{"name":"mongo-csharp-driver","version":"3.5.0"},"os":{"type":"Linux","name":"Ubuntu 24.04.3 LTS","architecture":"x86_64","version":"24.04.3"},"platform":".NET 10.0.0-rc.2.25502.107"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.859+00:00"},"s":"I", "c":"NETWORK", "id":22943, "ctx":"listener","msg":"Connection accepted","attr":{"remote":"127.0.0.1:47448","connectionId":6,"connectionCount":6}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.859+00:00"},"s":"I", "c":"NETWORK", "id":51800, "ctx":"conn6","msg":"client metadata","attr":{"remote":"127.0.0.1:47448","client":"conn6","doc":{"driver":{"name":"mongo-csharp-driver","version":"3.5.0"},"os":{"type":"Linux","name":"Ubuntu 24.04.3 LTS","architecture":"x86_64","version":"24.04.3"},"platform":".NET 10.0.0-rc.2.25502.107"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.860+00:00"},"s":"I", "c":"INDEX", "id":20438, "ctx":"conn5","msg":"Index build: registering","attr":{"buildUUID":{"uuid":{"$uuid":"8634f626-1a30-4a22-93a9-65dc7b3e7493"}},"namespace":"concelier.document","collectionUUID":{"uuid":{"$uuid":"ad2a5b15-8e65-4ed1-9efa-d0fd1e643131"}},"indexes":3,"firstIndex":{"name":"document_source_uri_unique"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.860+00:00"},"s":"I", "c":"INDEX", "id":20438, "ctx":"conn6","msg":"Index build: registering","attr":{"buildUUID":{"uuid":{"$uuid":"9ebafb07-90b6-47d0-9e2c-257bf2f104f7"}},"namespace":"concelier.dto","collectionUUID":{"uuid":{"$uuid":"81a72f1b-58d0-4a25-a0bc-0dd9362247f8"}},"indexes":2,"firstIndex":{"name":"dto_documentId"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.860+00:00"},"s":"I", "c":"NETWORK", "id":22943, "ctx":"listener","msg":"Connection accepted","attr":{"remote":"127.0.0.1:47460","connectionId":7,"connectionCount":7}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.861+00:00"},"s":"I", "c":"NETWORK", "id":22943, "ctx":"listener","msg":"Connection accepted","attr":{"remote":"127.0.0.1:47464","connectionId":8,"connectionCount":8}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.861+00:00"},"s":"I", "c":"NETWORK", "id":51800, "ctx":"conn7","msg":"client metadata","attr":{"remote":"127.0.0.1:47460","client":"conn7","doc":{"driver":{"name":"mongo-csharp-driver","version":"3.5.0"},"os":{"type":"Linux","name":"Ubuntu 24.04.3 LTS","architecture":"x86_64","version":"24.04.3"},"platform":".NET 10.0.0-rc.2.25502.107"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.861+00:00"},"s":"I", "c":"NETWORK", "id":51800, "ctx":"conn8","msg":"client metadata","attr":{"remote":"127.0.0.1:47464","client":"conn8","doc":{"driver":{"name":"mongo-csharp-driver","version":"3.5.0"},"os":{"type":"Linux","name":"Ubuntu 24.04.3 LTS","architecture":"x86_64","version":"24.04.3"},"platform":".NET 10.0.0-rc.2.25502.107"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.862+00:00"},"s":"I", "c":"INDEX", "id":20438, "ctx":"conn7","msg":"Index build: registering","attr":{"buildUUID":{"uuid":{"$uuid":"a56fab5b-f9c9-47ab-a907-c260047bad5e"}},"namespace":"concelier.alias","collectionUUID":{"uuid":{"$uuid":"6a6a3cc5-2ba2-4756-bf3f-197fd1a306a0"}},"indexes":1,"firstIndex":{"name":"alias_scheme_value"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.862+00:00"},"s":"I", "c":"INDEX", "id":20438, "ctx":"conn8","msg":"Index build: registering","attr":{"buildUUID":{"uuid":{"$uuid":"7df22170-a963-4a06-b173-cde909e8764c"}},"namespace":"concelier.affected","collectionUUID":{"uuid":{"$uuid":"ef930a9b-1097-41f9-9d77-2659520d64dc"}},"indexes":2,"firstIndex":{"name":"affected_platform_name"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.871+00:00"},"s":"I", "c":"NETWORK", "id":22943, "ctx":"listener","msg":"Connection accepted","attr":{"remote":"127.0.0.1:47476","connectionId":9,"connectionCount":9}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.871+00:00"},"s":"I", "c":"NETWORK", "id":22943, "ctx":"listener","msg":"Connection accepted","attr":{"remote":"127.0.0.1:47488","connectionId":10,"connectionCount":10}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.871+00:00"},"s":"I", "c":"NETWORK", "id":51800, "ctx":"conn9","msg":"client metadata","attr":{"remote":"127.0.0.1:47476","client":"conn9","doc":{"driver":{"name":"mongo-csharp-driver","version":"3.5.0"},"os":{"type":"Linux","name":"Ubuntu 24.04.3 LTS","architecture":"x86_64","version":"24.04.3"},"platform":".NET 10.0.0-rc.2.25502.107"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.872+00:00"},"s":"I", "c":"NETWORK", "id":51800, "ctx":"conn10","msg":"client metadata","attr":{"remote":"127.0.0.1:47488","client":"conn10","doc":{"driver":{"name":"mongo-csharp-driver","version":"3.5.0"},"os":{"type":"Linux","name":"Ubuntu 24.04.3 LTS","architecture":"x86_64","version":"24.04.3"},"platform":".NET 10.0.0-rc.2.25502.107"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.872+00:00"},"s":"I", "c":"INDEX", "id":20438, "ctx":"conn9","msg":"Index build: registering","attr":{"buildUUID":{"uuid":{"$uuid":"e75019bf-293c-4d90-bfa3-90e20b305975"}},"namespace":"concelier.source_state","collectionUUID":{"uuid":{"$uuid":"9475ed88-bc52-4c6d-abcb-5dfaf2d5cf5b"}},"indexes":1,"firstIndex":{"name":"source_state_unique"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.873+00:00"},"s":"I", "c":"INDEX", "id":20438, "ctx":"conn10","msg":"Index build: registering","attr":{"buildUUID":{"uuid":{"$uuid":"25b0858f-8e1d-43bc-afab-07712ea8e760"}},"namespace":"concelier.reference","collectionUUID":{"uuid":{"$uuid":"24d0213b-0677-42fa-b7ae-b0a19b36317d"}},"indexes":2,"firstIndex":{"name":"reference_url"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.876+00:00"},"s":"I", "c":"NETWORK", "id":22943, "ctx":"listener","msg":"Connection accepted","attr":{"remote":"127.0.0.1:47504","connectionId":11,"connectionCount":11}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.876+00:00"},"s":"I", "c":"NETWORK", "id":51800, "ctx":"conn11","msg":"client metadata","attr":{"remote":"127.0.0.1:47504","client":"conn11","doc":{"driver":{"name":"mongo-csharp-driver","version":"3.5.0"},"os":{"type":"Linux","name":"Ubuntu 24.04.3 LTS","architecture":"x86_64","version":"24.04.3"},"platform":".NET 10.0.0-rc.2.25502.107"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.878+00:00"},"s":"I", "c":"NETWORK", "id":22943, "ctx":"listener","msg":"Connection accepted","attr":{"remote":"127.0.0.1:47506","connectionId":12,"connectionCount":12}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.878+00:00"},"s":"I", "c":"COMMAND", "id":51806, "ctx":"conn11","msg":"CMD: dropIndexes","attr":{"namespace":"concelier.psirt_flags","uuid":{"uuid":{"$uuid":"d61fab06-e185-4905-a581-78d6188f9cbf"}},"indexes":"\"psirt_advisoryKey_unique\""}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.878+00:00"},"s":"I", "c":"NETWORK", "id":51800, "ctx":"conn12","msg":"client metadata","attr":{"remote":"127.0.0.1:47506","client":"conn12","doc":{"driver":{"name":"mongo-csharp-driver","version":"3.5.0"},"os":{"type":"Linux","name":"Ubuntu 24.04.3 LTS","architecture":"x86_64","version":"24.04.3"},"platform":".NET 10.0.0-rc.2.25502.107"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.879+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn4","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.jobs","index":"jobs_createdAt_desc","commitTimestamp":{"$timestamp":{"t":1764040015,"i":26}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.879+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn4","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.jobs","index":"jobs_kind_createdAt","commitTimestamp":{"$timestamp":{"t":1764040015,"i":26}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.879+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn4","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.jobs","index":"jobs_status_createdAt","commitTimestamp":{"$timestamp":{"t":1764040015,"i":26}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.879+00:00"},"s":"I", "c":"INDEX", "id":20440, "ctx":"conn4","msg":"Index build: waiting for index build to complete","attr":{"buildUUID":{"uuid":{"$uuid":"1146b67d-4236-4bc9-bae4-3fa891517889"}},"deadline":{"$date":{"$numberLong":"9223372036854775807"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.879+00:00"},"s":"I", "c":"NETWORK", "id":22943, "ctx":"listener","msg":"Connection accepted","attr":{"remote":"127.0.0.1:47514","connectionId":13,"connectionCount":13}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.879+00:00"},"s":"I", "c":"INDEX", "id":20438, "ctx":"conn12","msg":"Index build: registering","attr":{"buildUUID":{"uuid":{"$uuid":"e231aaa5-d5f8-4c88-9860-fe69d60d65f5"}},"namespace":"concelier.advisory_statements","collectionUUID":{"uuid":{"$uuid":"46b5cd3a-fd22-47d2-81cc-2c756d9cfe62"}},"indexes":2,"firstIndex":{"name":"advisory_statements_vulnerability_asof_desc"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.879+00:00"},"s":"I", "c":"INDEX", "id":20447, "ctx":"conn4","msg":"Index build: completed","attr":{"buildUUID":{"uuid":{"$uuid":"1146b67d-4236-4bc9-bae4-3fa891517889"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.880+00:00"},"s":"I", "c":"NETWORK", "id":51800, "ctx":"conn13","msg":"client metadata","attr":{"remote":"127.0.0.1:47514","client":"conn13","doc":{"driver":{"name":"mongo-csharp-driver","version":"3.5.0"},"os":{"type":"Linux","name":"Ubuntu 24.04.3 LTS","architecture":"x86_64","version":"24.04.3"},"platform":".NET 10.0.0-rc.2.25502.107"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.881+00:00"},"s":"I", "c":"NETWORK", "id":22943, "ctx":"listener","msg":"Connection accepted","attr":{"remote":"127.0.0.1:47524","connectionId":14,"connectionCount":14}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.881+00:00"},"s":"I", "c":"INDEX", "id":20438, "ctx":"conn13","msg":"Index build: registering","attr":{"buildUUID":{"uuid":{"$uuid":"eba85195-e631-4fb2-a8ba-d155fcbe0411"}},"namespace":"concelier.advisory_conflicts","collectionUUID":{"uuid":{"$uuid":"e830a702-eb38-4e79-bd71-139b63066228"}},"indexes":2,"firstIndex":{"name":"advisory_conflicts_vulnerability_asof_desc"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.881+00:00"},"s":"I", "c":"NETWORK", "id":51800, "ctx":"conn14","msg":"client metadata","attr":{"remote":"127.0.0.1:47524","client":"conn14","doc":{"driver":{"name":"mongo-csharp-driver","version":"3.5.0"},"os":{"type":"Linux","name":"Ubuntu 24.04.3 LTS","architecture":"x86_64","version":"24.04.3"},"platform":".NET 10.0.0-rc.2.25502.107"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.882+00:00"},"s":"I", "c":"NETWORK", "id":22943, "ctx":"listener","msg":"Connection accepted","attr":{"remote":"127.0.0.1:47538","connectionId":15,"connectionCount":15}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.882+00:00"},"s":"I", "c":"INDEX", "id":20438, "ctx":"conn14","msg":"Index build: registering","attr":{"buildUUID":{"uuid":{"$uuid":"054c0484-e72e-411f-bced-3f555ef0d361"}},"namespace":"concelier.advisory_observations","collectionUUID":{"uuid":{"$uuid":"2d30c6a9-a970-4507-9548-c93174011df9"}},"indexes":4,"firstIndex":{"name":"advisory_obs_tenant_upstream"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.882+00:00"},"s":"I", "c":"NETWORK", "id":51800, "ctx":"conn15","msg":"client metadata","attr":{"remote":"127.0.0.1:47538","client":"conn15","doc":{"driver":{"name":"mongo-csharp-driver","version":"3.5.0"},"os":{"type":"Linux","name":"Ubuntu 24.04.3 LTS","architecture":"x86_64","version":"24.04.3"},"platform":".NET 10.0.0-rc.2.25502.107"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.883+00:00"},"s":"I", "c":"INDEX", "id":20438, "ctx":"conn15","msg":"Index build: registering","attr":{"buildUUID":{"uuid":{"$uuid":"9756e330-8423-4878-bd4f-a3e1a8400472"}},"namespace":"concelier.source_change_history","collectionUUID":{"uuid":{"$uuid":"0c0938b6-7eb1-4e92-a8a8-5ed971581ddc"}},"indexes":3,"firstIndex":{"name":"history_source_advisory_capturedAt"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.883+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn4","msg":"createCollection","attr":{"namespace":"concelier.documents.files","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"c6f88ce0-e49c-4b58-aa67-0a5021c6c7b1"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.928+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn4","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.documents.files","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040015,"i":31}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.928+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn4","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.documents.files","index":"gridfs_files_expiresAt_ttl","commitTimestamp":{"$timestamp":{"t":1764040015,"i":31}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.945+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.advisory","index":"advisory_key_unique","commitTimestamp":{"$timestamp":{"t":1764040015,"i":33}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.945+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.advisory","index":"advisory_modified_desc","commitTimestamp":{"$timestamp":{"t":1764040015,"i":33}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.945+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.advisory","index":"advisory_published_desc","commitTimestamp":{"$timestamp":{"t":1764040015,"i":33}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.945+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.advisory","index":"advisory_normalizedVersions_pkg_scheme_type","commitTimestamp":{"$timestamp":{"t":1764040015,"i":33}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.945+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn3","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.advisory","index":"advisory_normalizedVersions_value","commitTimestamp":{"$timestamp":{"t":1764040015,"i":33}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.945+00:00"},"s":"I", "c":"INDEX", "id":20440, "ctx":"conn3","msg":"Index build: waiting for index build to complete","attr":{"buildUUID":{"uuid":{"$uuid":"b267ade3-39a8-4744-8ffe-e091e3a60a76"}},"deadline":{"$date":{"$numberLong":"9223372036854775807"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.945+00:00"},"s":"I", "c":"INDEX", "id":20447, "ctx":"conn3","msg":"Index build: completed","attr":{"buildUUID":{"uuid":{"$uuid":"b267ade3-39a8-4744-8ffe-e091e3a60a76"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.968+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn6","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.dto","index":"dto_documentId","commitTimestamp":{"$timestamp":{"t":1764040015,"i":35}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.968+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn6","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.dto","index":"dto_source_validated","commitTimestamp":{"$timestamp":{"t":1764040015,"i":35}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.968+00:00"},"s":"I", "c":"INDEX", "id":20440, "ctx":"conn6","msg":"Index build: waiting for index build to complete","attr":{"buildUUID":{"uuid":{"$uuid":"9ebafb07-90b6-47d0-9e2c-257bf2f104f7"}},"deadline":{"$date":{"$numberLong":"9223372036854775807"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.968+00:00"},"s":"I", "c":"INDEX", "id":20447, "ctx":"conn6","msg":"Index build: completed","attr":{"buildUUID":{"uuid":{"$uuid":"9ebafb07-90b6-47d0-9e2c-257bf2f104f7"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.974+00:00"},"s":"I", "c":"COMMAND", "id":51803, "ctx":"conn6","msg":"Slow query","attr":{"type":"command","ns":"concelier.dto","command":{"createIndexes":"dto","indexes":[{"key":{"documentId":1},"name":"dto_documentId"},{"key":{"sourceName":1,"validatedAt":-1},"name":"dto_source_validated"}],"writeConcern":{"w":"majority","wtimeout":30000.0},"$db":"concelier","lsid":{"id":{"$uuid":"0dce06ab-6c9e-44d5-a568-2c08aeae4f70"}},"$clusterTime":{"clusterTime":{"$timestamp":{"t":1764040015,"i":24}},"signature":{"hash":{"$binary":{"base64":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","subType":"0"}},"keyId":0}}},"numYields":0,"reslen":271,"locks":{"ParallelBatchWriterMode":{"acquireCount":{"r":3}},"ReplicationStateTransition":{"acquireCount":{"w":6}},"Global":{"acquireCount":{"r":2,"w":4}},"Database":{"acquireCount":{"w":3}},"Collection":{"acquireCount":{"r":1,"w":1,"W":1}},"Mutex":{"acquireCount":{"r":3}}},"flowControl":{"acquireCount":3,"timeAcquiringMicros":2},"writeConcern":{"w":"majority","wtimeout":30000,"provenance":"clientSupplied"},"storage":{},"protocol":"op_msg","durationMillis":113}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.983+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn9","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.source_state","index":"source_state_unique","commitTimestamp":{"$timestamp":{"t":1764040015,"i":36}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.983+00:00"},"s":"I", "c":"INDEX", "id":20440, "ctx":"conn9","msg":"Index build: waiting for index build to complete","attr":{"buildUUID":{"uuid":{"$uuid":"e75019bf-293c-4d90-bfa3-90e20b305975"}},"deadline":{"$date":{"$numberLong":"9223372036854775807"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.983+00:00"},"s":"I", "c":"INDEX", "id":20447, "ctx":"conn9","msg":"Index build: completed","attr":{"buildUUID":{"uuid":{"$uuid":"e75019bf-293c-4d90-bfa3-90e20b305975"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:55.988+00:00"},"s":"I", "c":"COMMAND", "id":51803, "ctx":"conn9","msg":"Slow query","attr":{"type":"command","ns":"concelier.source_state","command":{"createIndexes":"source_state","indexes":[{"key":{"sourceName":1},"name":"source_state_unique","unique":true}],"writeConcern":{"w":"majority","wtimeout":30000.0},"$db":"concelier","lsid":{"id":{"$uuid":"8671be39-6be8-4a57-932e-fcddeacacfc5"}},"$clusterTime":{"clusterTime":{"$timestamp":{"t":1764040015,"i":24}},"signature":{"hash":{"$binary":{"base64":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","subType":"0"}},"keyId":0}}},"numYields":0,"reslen":271,"locks":{"ParallelBatchWriterMode":{"acquireCount":{"r":3}},"ReplicationStateTransition":{"acquireCount":{"w":6}},"Global":{"acquireCount":{"r":2,"w":4}},"Database":{"acquireCount":{"w":3}},"Collection":{"acquireCount":{"r":1,"w":1,"W":1}},"Mutex":{"acquireCount":{"r":3}}},"flowControl":{"acquireCount":3,"timeAcquiringMicros":1},"writeConcern":{"w":"majority","wtimeout":30000,"provenance":"clientSupplied"},"storage":{},"protocol":"op_msg","durationMillis":116}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.016+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn5","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.document","index":"document_source_uri_unique","commitTimestamp":{"$timestamp":{"t":1764040016,"i":1}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.016+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn5","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.document","index":"document_fetchedAt_desc","commitTimestamp":{"$timestamp":{"t":1764040016,"i":1}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.016+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn5","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.document","index":"document_expiresAt_ttl","commitTimestamp":{"$timestamp":{"t":1764040016,"i":1}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.016+00:00"},"s":"I", "c":"INDEX", "id":20440, "ctx":"conn5","msg":"Index build: waiting for index build to complete","attr":{"buildUUID":{"uuid":{"$uuid":"8634f626-1a30-4a22-93a9-65dc7b3e7493"}},"deadline":{"$date":{"$numberLong":"9223372036854775807"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.016+00:00"},"s":"I", "c":"INDEX", "id":20447, "ctx":"conn5","msg":"Index build: completed","attr":{"buildUUID":{"uuid":{"$uuid":"8634f626-1a30-4a22-93a9-65dc7b3e7493"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.016+00:00"},"s":"I", "c":"STORAGE", "id":4715500, "ctx":"conn14","msg":"Too many index builds running simultaneously, waiting until the number of active index builds is below the threshold","attr":{"numActiveIndexBuilds":3,"maxNumActiveUserIndexBuilds":3,"indexSpecs":[{"key":{"tenant":1,"upstream.upstream_id":1,"upstream.document_version":1},"name":"advisory_obs_tenant_upstream","unique":false,"v":2},{"key":{"tenant":1,"linkset.aliases":1},"name":"advisory_obs_tenant_aliases","v":2},{"key":{"tenant":1,"linkset.purls":1},"name":"advisory_obs_tenant_purls","v":2},{"key":{"tenant":1,"createdAt":-1},"name":"advisory_obs_tenant_createdAt","v":2}],"buildUUID":{"uuid":{"$uuid":"054c0484-e72e-411f-bced-3f555ef0d361"}},"collectionUUID":{"uuid":{"$uuid":"2d30c6a9-a970-4507-9548-c93174011df9"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.017+00:00"},"s":"I", "c":"STORAGE", "id":4715500, "ctx":"conn15","msg":"Too many index builds running simultaneously, waiting until the number of active index builds is below the threshold","attr":{"numActiveIndexBuilds":3,"maxNumActiveUserIndexBuilds":3,"indexSpecs":[{"key":{"source":1,"advisoryKey":1,"capturedAt":-1},"name":"history_source_advisory_capturedAt","v":2},{"key":{"capturedAt":-1},"name":"history_capturedAt","v":2},{"key":{"documentId":1},"name":"history_documentId","v":2}],"buildUUID":{"uuid":{"$uuid":"9756e330-8423-4878-bd4f-a3e1a8400472"}},"collectionUUID":{"uuid":{"$uuid":"0c0938b6-7eb1-4e92-a8a8-5ed971581ddc"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.017+00:00"},"s":"I", "c":"STORAGE", "id":4715500, "ctx":"conn15","msg":"Too many index builds running simultaneously, waiting until the number of active index builds is below the threshold","attr":{"numActiveIndexBuilds":3,"maxNumActiveUserIndexBuilds":3,"indexSpecs":[{"key":{"source":1,"advisoryKey":1,"capturedAt":-1},"name":"history_source_advisory_capturedAt","v":2},{"key":{"capturedAt":-1},"name":"history_capturedAt","v":2},{"key":{"documentId":1},"name":"history_documentId","v":2}],"buildUUID":{"uuid":{"$uuid":"9756e330-8423-4878-bd4f-a3e1a8400472"}},"collectionUUID":{"uuid":{"$uuid":"0c0938b6-7eb1-4e92-a8a8-5ed971581ddc"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.017+00:00"},"s":"I", "c":"STORAGE", "id":4715500, "ctx":"conn8","msg":"Too many index builds running simultaneously, waiting until the number of active index builds is below the threshold","attr":{"numActiveIndexBuilds":3,"maxNumActiveUserIndexBuilds":3,"indexSpecs":[{"key":{"platform":1,"name":1},"name":"affected_platform_name","v":2},{"key":{"advisoryId":1},"name":"affected_advisoryId","v":2}],"buildUUID":{"uuid":{"$uuid":"7df22170-a963-4a06-b173-cde909e8764c"}},"collectionUUID":{"uuid":{"$uuid":"ef930a9b-1097-41f9-9d77-2659520d64dc"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.017+00:00"},"s":"I", "c":"STORAGE", "id":4715500, "ctx":"conn8","msg":"Too many index builds running simultaneously, waiting until the number of active index builds is below the threshold","attr":{"numActiveIndexBuilds":3,"maxNumActiveUserIndexBuilds":3,"indexSpecs":[{"key":{"platform":1,"name":1},"name":"affected_platform_name","v":2},{"key":{"advisoryId":1},"name":"affected_advisoryId","v":2}],"buildUUID":{"uuid":{"$uuid":"7df22170-a963-4a06-b173-cde909e8764c"}},"collectionUUID":{"uuid":{"$uuid":"ef930a9b-1097-41f9-9d77-2659520d64dc"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.018+00:00"},"s":"I", "c":"COMMAND", "id":51803, "ctx":"conn5","msg":"Slow query","attr":{"type":"command","ns":"concelier.document","command":{"createIndexes":"document","indexes":[{"key":{"sourceName":1,"uri":1},"name":"document_source_uri_unique","unique":true},{"key":{"fetchedAt":-1},"name":"document_fetchedAt_desc"},{"key":{"expiresAt":1},"name":"document_expiresAt_ttl","expireAfterSeconds":0.0,"partialFilterExpression":{"expiresAt":{"$exists":true}}}],"writeConcern":{"w":"majority","wtimeout":30000.0},"$db":"concelier","lsid":{"id":{"$uuid":"d31918ca-399a-4f47-8207-80777cac4b29"}},"$clusterTime":{"clusterTime":{"$timestamp":{"t":1764040015,"i":24}},"signature":{"hash":{"$binary":{"base64":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","subType":"0"}},"keyId":0}}},"numYields":0,"reslen":271,"locks":{"ParallelBatchWriterMode":{"acquireCount":{"r":3}},"ReplicationStateTransition":{"acquireCount":{"w":6}},"Global":{"acquireCount":{"r":2,"w":4}},"Database":{"acquireCount":{"w":3}},"Collection":{"acquireCount":{"r":1,"w":1,"W":1}},"Mutex":{"acquireCount":{"r":3}}},"flowControl":{"acquireCount":3,"timeAcquiringMicros":3},"writeConcern":{"w":"majority","wtimeout":30000,"provenance":"clientSupplied"},"storage":{},"protocol":"op_msg","durationMillis":158}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.020+00:00"},"s":"I", "c":"COMMAND", "id":51803, "ctx":"conn11","msg":"Slow query","attr":{"type":"command","ns":"concelier.psirt_flags","command":{"dropIndexes":"psirt_flags","index":"psirt_advisoryKey_unique","writeConcern":{"w":"majority","wtimeout":30000.0},"$db":"concelier","lsid":{"id":{"$uuid":"12d8a496-37e2-46f8-8e2f-a41a2f99ac09"}},"$clusterTime":{"clusterTime":{"$timestamp":{"t":1764040015,"i":24}},"signature":{"hash":{"$binary":{"base64":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","subType":"0"}},"keyId":0}}},"numYields":0,"ok":0,"errMsg":"index not found with name [psirt_advisoryKey_unique]","errName":"IndexNotFound","errCode":27,"reslen":266,"locks":{"ParallelBatchWriterMode":{"acquireCount":{"r":2}},"ReplicationStateTransition":{"acquireCount":{"w":4}},"Global":{"acquireCount":{"r":2,"w":2}},"Database":{"acquireCount":{"w":2}},"Collection":{"acquireCount":{"w":1,"W":1}},"Mutex":{"acquireCount":{"r":3}}},"flowControl":{"acquireCount":2,"timeAcquiringMicros":1},"writeConcern":{"w":"majority","wtimeout":30000,"provenance":"clientSupplied"},"storage":{},"protocol":"op_msg","durationMillis":141}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.031+00:00"},"s":"I", "c":"INDEX", "id":20438, "ctx":"conn11","msg":"Index build: registering","attr":{"buildUUID":{"uuid":{"$uuid":"59b6cebf-aee3-46b7-814a-856404eb982d"}},"namespace":"concelier.psirt_flags","collectionUUID":{"uuid":{"$uuid":"d61fab06-e185-4905-a581-78d6188f9cbf"}},"indexes":1,"firstIndex":{"name":"psirt_vendor"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.035+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn10","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.reference","index":"reference_url","commitTimestamp":{"$timestamp":{"t":1764040016,"i":3}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.035+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn10","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.reference","index":"reference_advisoryId","commitTimestamp":{"$timestamp":{"t":1764040016,"i":3}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.035+00:00"},"s":"I", "c":"INDEX", "id":20440, "ctx":"conn10","msg":"Index build: waiting for index build to complete","attr":{"buildUUID":{"uuid":{"$uuid":"25b0858f-8e1d-43bc-afab-07712ea8e760"}},"deadline":{"$date":{"$numberLong":"9223372036854775807"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.035+00:00"},"s":"I", "c":"INDEX", "id":20447, "ctx":"conn10","msg":"Index build: completed","attr":{"buildUUID":{"uuid":{"$uuid":"25b0858f-8e1d-43bc-afab-07712ea8e760"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.037+00:00"},"s":"I", "c":"COMMAND", "id":51803, "ctx":"conn10","msg":"Slow query","attr":{"type":"command","ns":"concelier.reference","command":{"createIndexes":"reference","indexes":[{"key":{"url":1},"name":"reference_url"},{"key":{"advisoryId":1},"name":"reference_advisoryId"}],"writeConcern":{"w":"majority","wtimeout":30000.0},"$db":"concelier","lsid":{"id":{"$uuid":"e8db91b1-ad7d-4cb3-a86b-47d5b309fc80"}},"$clusterTime":{"clusterTime":{"$timestamp":{"t":1764040015,"i":24}},"signature":{"hash":{"$binary":{"base64":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","subType":"0"}},"keyId":0}}},"numYields":0,"reslen":271,"locks":{"ParallelBatchWriterMode":{"acquireCount":{"r":3}},"ReplicationStateTransition":{"acquireCount":{"w":6}},"Global":{"acquireCount":{"r":2,"w":4}},"Database":{"acquireCount":{"w":3}},"Collection":{"acquireCount":{"r":1,"w":1,"W":1}},"Mutex":{"acquireCount":{"r":3}}},"flowControl":{"acquireCount":3,"timeAcquiringMicros":2},"writeConcern":{"w":"majority","wtimeout":30000,"provenance":"clientSupplied"},"storage":{},"protocol":"op_msg","durationMillis":164}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.051+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn13","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.advisory_conflicts","index":"advisory_conflicts_vulnerability_asof_desc","commitTimestamp":{"$timestamp":{"t":1764040016,"i":5}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.051+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn13","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.advisory_conflicts","index":"advisory_conflicts_conflictHash_unique","commitTimestamp":{"$timestamp":{"t":1764040016,"i":5}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.051+00:00"},"s":"I", "c":"INDEX", "id":20440, "ctx":"conn13","msg":"Index build: waiting for index build to complete","attr":{"buildUUID":{"uuid":{"$uuid":"eba85195-e631-4fb2-a8ba-d155fcbe0411"}},"deadline":{"$date":{"$numberLong":"9223372036854775807"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.051+00:00"},"s":"I", "c":"INDEX", "id":20447, "ctx":"conn13","msg":"Index build: completed","attr":{"buildUUID":{"uuid":{"$uuid":"eba85195-e631-4fb2-a8ba-d155fcbe0411"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.053+00:00"},"s":"I", "c":"COMMAND", "id":51803, "ctx":"conn13","msg":"Slow query","attr":{"type":"command","ns":"concelier.advisory_conflicts","command":{"createIndexes":"advisory_conflicts","indexes":[{"key":{"vulnerabilityKey":1,"asOf":-1},"name":"advisory_conflicts_vulnerability_asof_desc"},{"key":{"conflictHash":1},"name":"advisory_conflicts_conflictHash_unique","unique":true}],"writeConcern":{"w":"majority","wtimeout":30000.0},"$db":"concelier","lsid":{"id":{"$uuid":"92e1dc41-2888-47f4-a1dc-abd349a494a4"}},"$clusterTime":{"clusterTime":{"$timestamp":{"t":1764040015,"i":24}},"signature":{"hash":{"$binary":{"base64":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","subType":"0"}},"keyId":0}}},"numYields":0,"reslen":271,"locks":{"ParallelBatchWriterMode":{"acquireCount":{"r":3}},"ReplicationStateTransition":{"acquireCount":{"w":6}},"Global":{"acquireCount":{"r":2,"w":4}},"Database":{"acquireCount":{"w":3}},"Collection":{"acquireCount":{"r":1,"w":1,"W":1}},"Mutex":{"acquireCount":{"r":3}}},"flowControl":{"acquireCount":3,"timeAcquiringMicros":2},"writeConcern":{"w":"majority","wtimeout":30000,"provenance":"clientSupplied"},"storage":{},"protocol":"op_msg","durationMillis":172}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.059+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn7","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.alias","index":"alias_scheme_value","commitTimestamp":{"$timestamp":{"t":1764040016,"i":6}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.059+00:00"},"s":"I", "c":"INDEX", "id":20440, "ctx":"conn7","msg":"Index build: waiting for index build to complete","attr":{"buildUUID":{"uuid":{"$uuid":"a56fab5b-f9c9-47ab-a907-c260047bad5e"}},"deadline":{"$date":{"$numberLong":"9223372036854775807"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.059+00:00"},"s":"I", "c":"INDEX", "id":20447, "ctx":"conn7","msg":"Index build: completed","attr":{"buildUUID":{"uuid":{"$uuid":"a56fab5b-f9c9-47ab-a907-c260047bad5e"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.059+00:00"},"s":"I", "c":"STORAGE", "id":4715500, "ctx":"conn14","msg":"Too many index builds running simultaneously, waiting until the number of active index builds is below the threshold","attr":{"numActiveIndexBuilds":3,"maxNumActiveUserIndexBuilds":3,"indexSpecs":[{"key":{"tenant":1,"upstream.upstream_id":1,"upstream.document_version":1},"name":"advisory_obs_tenant_upstream","unique":false,"v":2},{"key":{"tenant":1,"linkset.aliases":1},"name":"advisory_obs_tenant_aliases","v":2},{"key":{"tenant":1,"linkset.purls":1},"name":"advisory_obs_tenant_purls","v":2},{"key":{"tenant":1,"createdAt":-1},"name":"advisory_obs_tenant_createdAt","v":2}],"buildUUID":{"uuid":{"$uuid":"054c0484-e72e-411f-bced-3f555ef0d361"}},"collectionUUID":{"uuid":{"$uuid":"2d30c6a9-a970-4507-9548-c93174011df9"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.059+00:00"},"s":"I", "c":"STORAGE", "id":4715500, "ctx":"conn14","msg":"Too many index builds running simultaneously, waiting until the number of active index builds is below the threshold","attr":{"numActiveIndexBuilds":3,"maxNumActiveUserIndexBuilds":3,"indexSpecs":[{"key":{"tenant":1,"upstream.upstream_id":1,"upstream.document_version":1},"name":"advisory_obs_tenant_upstream","unique":false,"v":2},{"key":{"tenant":1,"linkset.aliases":1},"name":"advisory_obs_tenant_aliases","v":2},{"key":{"tenant":1,"linkset.purls":1},"name":"advisory_obs_tenant_purls","v":2},{"key":{"tenant":1,"createdAt":-1},"name":"advisory_obs_tenant_createdAt","v":2}],"buildUUID":{"uuid":{"$uuid":"054c0484-e72e-411f-bced-3f555ef0d361"}},"collectionUUID":{"uuid":{"$uuid":"2d30c6a9-a970-4507-9548-c93174011df9"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.059+00:00"},"s":"I", "c":"STORAGE", "id":4715500, "ctx":"conn11","msg":"Too many index builds running simultaneously, waiting until the number of active index builds is below the threshold","attr":{"numActiveIndexBuilds":3,"maxNumActiveUserIndexBuilds":3,"indexSpecs":[{"key":{"vendor":1},"name":"psirt_vendor","v":2}],"buildUUID":{"uuid":{"$uuid":"59b6cebf-aee3-46b7-814a-856404eb982d"}},"collectionUUID":{"uuid":{"$uuid":"d61fab06-e185-4905-a581-78d6188f9cbf"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.059+00:00"},"s":"I", "c":"STORAGE", "id":4715500, "ctx":"conn11","msg":"Too many index builds running simultaneously, waiting until the number of active index builds is below the threshold","attr":{"numActiveIndexBuilds":3,"maxNumActiveUserIndexBuilds":3,"indexSpecs":[{"key":{"vendor":1},"name":"psirt_vendor","v":2}],"buildUUID":{"uuid":{"$uuid":"59b6cebf-aee3-46b7-814a-856404eb982d"}},"collectionUUID":{"uuid":{"$uuid":"d61fab06-e185-4905-a581-78d6188f9cbf"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.062+00:00"},"s":"I", "c":"COMMAND", "id":51803, "ctx":"conn7","msg":"Slow query","attr":{"type":"command","ns":"concelier.alias","command":{"createIndexes":"alias","indexes":[{"key":{"scheme":1,"value":1},"name":"alias_scheme_value","unique":false}],"writeConcern":{"w":"majority","wtimeout":30000.0},"$db":"concelier","lsid":{"id":{"$uuid":"9451e45a-666e-4afb-b7dc-24139346c68a"}},"$clusterTime":{"clusterTime":{"$timestamp":{"t":1764040015,"i":24}},"signature":{"hash":{"$binary":{"base64":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","subType":"0"}},"keyId":0}}},"numYields":0,"reslen":271,"locks":{"ParallelBatchWriterMode":{"acquireCount":{"r":3}},"ReplicationStateTransition":{"acquireCount":{"w":6}},"Global":{"acquireCount":{"r":2,"w":4}},"Database":{"acquireCount":{"w":3}},"Collection":{"acquireCount":{"r":1,"w":1,"W":1}},"Mutex":{"acquireCount":{"r":3}}},"flowControl":{"acquireCount":3,"timeAcquiringMicros":1},"writeConcern":{"w":"majority","wtimeout":30000,"provenance":"clientSupplied"},"storage":{},"protocol":"op_msg","durationMillis":199}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.076+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn8","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.affected","index":"affected_platform_name","commitTimestamp":{"$timestamp":{"t":1764040016,"i":8}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.076+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn8","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.affected","index":"affected_advisoryId","commitTimestamp":{"$timestamp":{"t":1764040016,"i":8}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.100+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn15","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.source_change_history","index":"history_source_advisory_capturedAt","commitTimestamp":{"$timestamp":{"t":1764040016,"i":11}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.100+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn15","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.source_change_history","index":"history_capturedAt","commitTimestamp":{"$timestamp":{"t":1764040016,"i":11}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.100+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn15","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.source_change_history","index":"history_documentId","commitTimestamp":{"$timestamp":{"t":1764040016,"i":11}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.101+00:00"},"s":"I", "c":"INDEX", "id":20440, "ctx":"conn15","msg":"Index build: waiting for index build to complete","attr":{"buildUUID":{"uuid":{"$uuid":"9756e330-8423-4878-bd4f-a3e1a8400472"}},"deadline":{"$date":{"$numberLong":"9223372036854775807"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.101+00:00"},"s":"I", "c":"INDEX", "id":20447, "ctx":"conn15","msg":"Index build: completed","attr":{"buildUUID":{"uuid":{"$uuid":"9756e330-8423-4878-bd4f-a3e1a8400472"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.103+00:00"},"s":"I", "c":"COMMAND", "id":51803, "ctx":"conn15","msg":"Slow query","attr":{"type":"command","ns":"concelier.source_change_history","command":{"createIndexes":"source_change_history","indexes":[{"key":{"source":1,"advisoryKey":1,"capturedAt":-1},"name":"history_source_advisory_capturedAt"},{"key":{"capturedAt":-1},"name":"history_capturedAt"},{"key":{"documentId":1},"name":"history_documentId"}],"writeConcern":{"w":"majority","wtimeout":30000.0},"$db":"concelier","lsid":{"id":{"$uuid":"8b32a551-8036-4a89-ab2e-c86d08aa9663"}},"$clusterTime":{"clusterTime":{"$timestamp":{"t":1764040015,"i":27}},"signature":{"hash":{"$binary":{"base64":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","subType":"0"}},"keyId":0}}},"numYields":0,"reslen":271,"locks":{"ParallelBatchWriterMode":{"acquireCount":{"r":3}},"ReplicationStateTransition":{"acquireCount":{"w":6}},"Global":{"acquireCount":{"r":2,"w":4}},"Database":{"acquireCount":{"w":3}},"Collection":{"acquireCount":{"r":1,"w":1,"W":1}},"Mutex":{"acquireCount":{"r":3}}},"flowControl":{"acquireCount":3,"timeAcquiringMicros":3},"writeConcern":{"w":"majority","wtimeout":30000,"provenance":"clientSupplied"},"storage":{},"protocol":"op_msg","durationMillis":220}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.132+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn14","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.advisory_observations","index":"advisory_obs_tenant_upstream","commitTimestamp":{"$timestamp":{"t":1764040016,"i":15}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.132+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn14","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.advisory_observations","index":"advisory_obs_tenant_aliases","commitTimestamp":{"$timestamp":{"t":1764040016,"i":15}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.132+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn14","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.advisory_observations","index":"advisory_obs_tenant_purls","commitTimestamp":{"$timestamp":{"t":1764040016,"i":15}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.132+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn14","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.advisory_observations","index":"advisory_obs_tenant_createdAt","commitTimestamp":{"$timestamp":{"t":1764040016,"i":15}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.132+00:00"},"s":"I", "c":"INDEX", "id":20440, "ctx":"conn14","msg":"Index build: waiting for index build to complete","attr":{"buildUUID":{"uuid":{"$uuid":"054c0484-e72e-411f-bced-3f555ef0d361"}},"deadline":{"$date":{"$numberLong":"9223372036854775807"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.132+00:00"},"s":"I", "c":"INDEX", "id":20447, "ctx":"conn14","msg":"Index build: completed","attr":{"buildUUID":{"uuid":{"$uuid":"054c0484-e72e-411f-bced-3f555ef0d361"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.137+00:00"},"s":"I", "c":"COMMAND", "id":51803, "ctx":"conn14","msg":"Slow query","attr":{"type":"command","ns":"concelier.advisory_observations","command":{"createIndexes":"advisory_observations","indexes":[{"key":{"tenant":1,"upstream.upstream_id":1,"upstream.document_version":1},"name":"advisory_obs_tenant_upstream","unique":false},{"key":{"tenant":1,"linkset.aliases":1},"name":"advisory_obs_tenant_aliases"},{"key":{"tenant":1,"linkset.purls":1},"name":"advisory_obs_tenant_purls"},{"key":{"tenant":1,"createdAt":-1},"name":"advisory_obs_tenant_createdAt"}],"writeConcern":{"w":"majority","wtimeout":30000.0},"$db":"concelier","lsid":{"id":{"$uuid":"959fef49-dc3d-44bf-824f-522cb94dcab9"}},"$clusterTime":{"clusterTime":{"$timestamp":{"t":1764040015,"i":24}},"signature":{"hash":{"$binary":{"base64":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","subType":"0"}},"keyId":0}}},"numYields":0,"reslen":271,"locks":{"ParallelBatchWriterMode":{"acquireCount":{"r":3}},"ReplicationStateTransition":{"acquireCount":{"w":6}},"Global":{"acquireCount":{"r":2,"w":4}},"Database":{"acquireCount":{"w":3}},"Collection":{"acquireCount":{"r":1,"w":1,"W":1}},"Mutex":{"acquireCount":{"r":3}}},"flowControl":{"acquireCount":3,"timeAcquiringMicros":1},"writeConcern":{"w":"majority","wtimeout":30000,"provenance":"clientSupplied"},"storage":{},"protocol":"op_msg","durationMillis":255}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.142+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn11","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.psirt_flags","index":"psirt_vendor","commitTimestamp":{"$timestamp":{"t":1764040016,"i":16}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.142+00:00"},"s":"I", "c":"INDEX", "id":20440, "ctx":"conn11","msg":"Index build: waiting for index build to complete","attr":{"buildUUID":{"uuid":{"$uuid":"59b6cebf-aee3-46b7-814a-856404eb982d"}},"deadline":{"$date":{"$numberLong":"9223372036854775807"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.142+00:00"},"s":"I", "c":"INDEX", "id":20447, "ctx":"conn11","msg":"Index build: completed","attr":{"buildUUID":{"uuid":{"$uuid":"59b6cebf-aee3-46b7-814a-856404eb982d"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.145+00:00"},"s":"I", "c":"COMMAND", "id":51803, "ctx":"conn11","msg":"Slow query","attr":{"type":"command","ns":"concelier.psirt_flags","command":{"createIndexes":"psirt_flags","indexes":[{"key":{"vendor":1},"name":"psirt_vendor"}],"writeConcern":{"w":"majority","wtimeout":30000.0},"$db":"concelier","lsid":{"id":{"$uuid":"12d8a496-37e2-46f8-8e2f-a41a2f99ac09"}},"$clusterTime":{"clusterTime":{"$timestamp":{"t":1764040016,"i":2}},"signature":{"hash":{"$binary":{"base64":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","subType":"0"}},"keyId":0}}},"numYields":0,"reslen":271,"locks":{"ParallelBatchWriterMode":{"acquireCount":{"r":3}},"ReplicationStateTransition":{"acquireCount":{"w":6}},"Global":{"acquireCount":{"r":2,"w":4}},"Database":{"acquireCount":{"w":3}},"Collection":{"acquireCount":{"r":1,"w":1,"W":1}},"Mutex":{"acquireCount":{"r":3}}},"flowControl":{"acquireCount":3,"timeAcquiringMicros":1},"writeConcern":{"w":"majority","wtimeout":30000,"provenance":"clientSupplied"},"storage":{},"protocol":"op_msg","durationMillis":113}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.158+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn12","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.advisory_statements","index":"advisory_statements_vulnerability_asof_desc","commitTimestamp":{"$timestamp":{"t":1764040016,"i":18}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.158+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn12","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.advisory_statements","index":"advisory_statements_statementHash_unique","commitTimestamp":{"$timestamp":{"t":1764040016,"i":18}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.158+00:00"},"s":"I", "c":"INDEX", "id":20440, "ctx":"conn12","msg":"Index build: waiting for index build to complete","attr":{"buildUUID":{"uuid":{"$uuid":"e231aaa5-d5f8-4c88-9860-fe69d60d65f5"}},"deadline":{"$date":{"$numberLong":"9223372036854775807"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.158+00:00"},"s":"I", "c":"INDEX", "id":20440, "ctx":"conn8","msg":"Index build: waiting for index build to complete","attr":{"buildUUID":{"uuid":{"$uuid":"7df22170-a963-4a06-b173-cde909e8764c"}},"deadline":{"$date":{"$numberLong":"9223372036854775807"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.158+00:00"},"s":"I", "c":"INDEX", "id":20447, "ctx":"conn12","msg":"Index build: completed","attr":{"buildUUID":{"uuid":{"$uuid":"e231aaa5-d5f8-4c88-9860-fe69d60d65f5"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.158+00:00"},"s":"I", "c":"INDEX", "id":20447, "ctx":"conn8","msg":"Index build: completed","attr":{"buildUUID":{"uuid":{"$uuid":"7df22170-a963-4a06-b173-cde909e8764c"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.160+00:00"},"s":"I", "c":"COMMAND", "id":51803, "ctx":"conn8","msg":"Slow query","attr":{"type":"command","ns":"concelier.affected","command":{"createIndexes":"affected","indexes":[{"key":{"platform":1,"name":1},"name":"affected_platform_name"},{"key":{"advisoryId":1},"name":"affected_advisoryId"}],"writeConcern":{"w":"majority","wtimeout":30000.0},"$db":"concelier","lsid":{"id":{"$uuid":"db62eb74-b9d0-420f-b476-36bfe600a00e"}},"$clusterTime":{"clusterTime":{"$timestamp":{"t":1764040015,"i":24}},"signature":{"hash":{"$binary":{"base64":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","subType":"0"}},"keyId":0}}},"numYields":0,"reslen":271,"locks":{"ParallelBatchWriterMode":{"acquireCount":{"r":3}},"ReplicationStateTransition":{"acquireCount":{"w":6}},"Global":{"acquireCount":{"r":2,"w":4}},"Database":{"acquireCount":{"w":3}},"Collection":{"acquireCount":{"r":1,"w":1,"W":1}},"Mutex":{"acquireCount":{"r":3}}},"flowControl":{"acquireCount":3,"timeAcquiringMicros":3},"writeConcern":{"w":"majority","wtimeout":30000,"provenance":"clientSupplied"},"storage":{},"protocol":"op_msg","durationMillis":297}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.160+00:00"},"s":"I", "c":"COMMAND", "id":51803, "ctx":"conn12","msg":"Slow query","attr":{"type":"command","ns":"concelier.advisory_statements","command":{"createIndexes":"advisory_statements","indexes":[{"key":{"vulnerabilityKey":1,"asOf":-1},"name":"advisory_statements_vulnerability_asof_desc"},{"key":{"statementHash":1},"name":"advisory_statements_statementHash_unique","unique":true}],"writeConcern":{"w":"majority","wtimeout":30000.0},"$db":"concelier","lsid":{"id":{"$uuid":"e30476f3-96d5-4a1b-b952-b9c3c8c48f05"}},"$clusterTime":{"clusterTime":{"$timestamp":{"t":1764040015,"i":24}},"signature":{"hash":{"$binary":{"base64":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","subType":"0"}},"keyId":0}}},"numYields":0,"reslen":271,"locks":{"ParallelBatchWriterMode":{"acquireCount":{"r":3}},"ReplicationStateTransition":{"acquireCount":{"w":6}},"Global":{"acquireCount":{"r":2,"w":4}},"Database":{"acquireCount":{"w":3}},"Collection":{"acquireCount":{"r":1,"w":1,"W":1}},"Mutex":{"acquireCount":{"r":3}}},"flowControl":{"acquireCount":3,"timeAcquiringMicros":2},"writeConcern":{"w":"majority","wtimeout":30000,"provenance":"clientSupplied"},"storage":{},"protocol":"op_msg","durationMillis":281}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.Migrations.MongoMigrationRunner[0]
|
||||
Applying Mongo migration 20241005_document_expiry_indexes: Ensure document.expiresAt index matches configured retention
|
||||
info: StellaOps.Concelier.Storage.Mongo.Migrations.MongoMigrationRunner[0]
|
||||
Mongo migration 20241005_document_expiry_indexes applied
|
||||
info: StellaOps.Concelier.Storage.Mongo.Migrations.MongoMigrationRunner[0]
|
||||
Applying Mongo migration 20241005_gridfs_expiry_indexes: Ensure GridFS metadata.expiresAt TTL index reflects retention settings
|
||||
info: StellaOps.Concelier.Storage.Mongo.Migrations.MongoMigrationRunner[0]
|
||||
Mongo migration 20241005_gridfs_expiry_indexes applied
|
||||
info: StellaOps.Concelier.Storage.Mongo.Migrations.MongoMigrationRunner[0]
|
||||
Applying Mongo migration 2025-11-07-advisory-canonical-key: Populate advisory_key and links for advisory_raw documents.
|
||||
info: StellaOps.Concelier.Storage.Mongo.Migrations.MongoMigrationRunner[0]
|
||||
Mongo migration 2025-11-07-advisory-canonical-key applied
|
||||
info: StellaOps.Concelier.Storage.Mongo.Migrations.MongoMigrationRunner[0]
|
||||
Applying Mongo migration 20251011-semver-style-backfill: Populate advisory.normalizedVersions for existing documents when SemVer style storage is enabled.
|
||||
info: StellaOps.Concelier.Storage.Mongo.Migrations.MongoMigrationRunner[0]
|
||||
Mongo migration 20251011-semver-style-backfill applied
|
||||
info: StellaOps.Concelier.Storage.Mongo.Migrations.MongoMigrationRunner[0]
|
||||
Applying Mongo migration 20251019_advisory_event_collections: Ensure advisory_statements and advisory_conflicts indexes exist for event log storage.
|
||||
info: StellaOps.Concelier.Storage.Mongo.Migrations.MongoMigrationRunner[0]
|
||||
Mongo migration 20251019_advisory_event_collections applied
|
||||
info: StellaOps.Concelier.Storage.Mongo.Migrations.MongoMigrationRunner[0]
|
||||
Applying Mongo migration 20251028_advisory_raw_idempotency_index: Ensure advisory_raw collection enforces idempotency via unique compound index.
|
||||
{"t":{"$date":"2025-11-25T03:06:56.373+00:00"},"s":"I", "c":"INDEX", "id":20438, "ctx":"conn12","msg":"Index build: registering","attr":{"buildUUID":{"uuid":{"$uuid":"d0d7de72-350c-4703-88fe-4604a6c0d70c"}},"namespace":"concelier.advisory_raw","collectionUUID":{"uuid":{"$uuid":"70542ec2-832b-4f93-8c96-4ca814f1fbbc"}},"indexes":1,"firstIndex":{"name":"advisory_raw_idempotency"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.381+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn12","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.advisory_raw","index":"advisory_raw_idempotency","commitTimestamp":{"$timestamp":{"t":1764040016,"i":24}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.381+00:00"},"s":"I", "c":"INDEX", "id":20440, "ctx":"conn12","msg":"Index build: waiting for index build to complete","attr":{"buildUUID":{"uuid":{"$uuid":"d0d7de72-350c-4703-88fe-4604a6c0d70c"}},"deadline":{"$date":{"$numberLong":"9223372036854775807"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.381+00:00"},"s":"I", "c":"INDEX", "id":20447, "ctx":"conn12","msg":"Index build: completed","attr":{"buildUUID":{"uuid":{"$uuid":"d0d7de72-350c-4703-88fe-4604a6c0d70c"}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.Migrations.MongoMigrationRunner[0]
|
||||
Mongo migration 20251028_advisory_raw_idempotency_index applied
|
||||
info: StellaOps.Concelier.Storage.Mongo.Migrations.MongoMigrationRunner[0]
|
||||
Applying Mongo migration 20251028_advisory_raw_validator: Ensure advisory_raw collection enforces Aggregation-Only Contract schema
|
||||
info: StellaOps.Concelier.Storage.Mongo.Migrations.MongoMigrationRunner[0]
|
||||
Mongo migration 20251028_advisory_raw_validator applied
|
||||
info: StellaOps.Concelier.Storage.Mongo.Migrations.MongoMigrationRunner[0]
|
||||
Applying Mongo migration 20251028_advisory_supersedes_backfill: Backfill advisory_raw supersedes chains and replace legacy advisory collection with read-only view.
|
||||
{"t":{"$date":"2025-11-25T03:06:56.422+00:00"},"s":"I", "c":"COMMAND", "id":20400, "ctx":"conn12","msg":"renameCollectionForCommand","attr":{"sourceNamespace":"concelier.advisory","targetNamespace":"concelier.advisory_backup_20251028","dropTarget":"no"}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.422+00:00"},"s":"I", "c":"STORAGE", "id":20319, "ctx":"conn12","msg":"renameCollection","attr":{"uuid":{"uuid":{"$uuid":"c2e4124c-bf80-4e3c-9272-cea8f40106f5"}},"fromName":"concelier.advisory","toName":"concelier.advisory_backup_20251028"}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.427+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn12","msg":"createCollection","attr":{"namespace":"concelier.system.views","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"11aeedd7-8f4c-4bf6-a15f-508c507370da"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.445+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn12","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.system.views","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040016,"i":29}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.Migrations.MongoMigrationRunner[0]
|
||||
Mongo migration 20251028_advisory_supersedes_backfill applied
|
||||
info: StellaOps.Concelier.Storage.Mongo.Migrations.MongoMigrationRunner[0]
|
||||
Applying Mongo migration 20251104_advisory_observations_raw_linkset: Populate rawLinkset field for advisory observations using stored advisory_raw documents.
|
||||
info: StellaOps.Concelier.Storage.Mongo.Migrations.MongoMigrationRunner[0]
|
||||
Mongo migration 20251104_advisory_observations_raw_linkset applied
|
||||
info: StellaOps.Concelier.Storage.Mongo.Migrations.MongoMigrationRunner[0]
|
||||
Applying Mongo migration 20251117_advisory_linksets_tenant_lower: Lowercase tenant ids in advisory_linksets to match query filters.
|
||||
info: StellaOps.Concelier.Storage.Mongo.Migrations.MongoMigrationRunner[0]
|
||||
Mongo migration 20251117_advisory_linksets_tenant_lower applied
|
||||
info: StellaOps.Concelier.Storage.Mongo.Migrations.MongoMigrationRunner[0]
|
||||
Applying Mongo migration 20251120_advisory_observation_events: Ensure advisory_observation_events collection and indexes exist for observation event fan-out.
|
||||
{"t":{"$date":"2025-11-25T03:06:56.489+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn12","msg":"createCollection","attr":{"namespace":"concelier.advisory_observation_events","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"2ee210ff-d50f-4a43-9d2b-8160e01daa2f"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.524+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn12","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.advisory_observation_events","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040016,"i":36}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.525+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn12","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.advisory_observation_events","index":"advisory_observation_events_tenant_ingested_desc","commitTimestamp":{"$timestamp":{"t":1764040016,"i":36}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.525+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn12","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.advisory_observation_events","index":"advisory_observation_events_hash_unique","commitTimestamp":{"$timestamp":{"t":1764040016,"i":36}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.Migrations.MongoMigrationRunner[0]
|
||||
Mongo migration 20251120_advisory_observation_events applied
|
||||
info: StellaOps.Concelier.Storage.Mongo.Migrations.MongoMigrationRunner[0]
|
||||
Applying Mongo migration 20251122_orchestrator_registry_commands: Ensure orchestrator registry, commands, and heartbeats collections exist with indexes
|
||||
{"t":{"$date":"2025-11-25T03:06:56.535+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn12","msg":"createCollection","attr":{"namespace":"concelier.orchestrator_registry","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"6649d503-b817-4ea5-88ce-a93b0536995d"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.551+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn12","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.orchestrator_registry","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040016,"i":38}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.554+00:00"},"s":"I", "c":"INDEX", "id":20438, "ctx":"conn12","msg":"Index build: registering","attr":{"buildUUID":{"uuid":{"$uuid":"0f95b012-1a7d-415e-9a84-9839c759b37e"}},"namespace":"concelier.orchestrator_registry","collectionUUID":{"uuid":{"$uuid":"6649d503-b817-4ea5-88ce-a93b0536995d"}},"indexes":2,"firstIndex":{"name":"orch_registry_tenant_connector"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.577+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn12","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.orchestrator_registry","index":"orch_registry_tenant_connector","commitTimestamp":{"$timestamp":{"t":1764040016,"i":40}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.577+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn12","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.orchestrator_registry","index":"orch_registry_source","commitTimestamp":{"$timestamp":{"t":1764040016,"i":40}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.577+00:00"},"s":"I", "c":"INDEX", "id":20440, "ctx":"conn12","msg":"Index build: waiting for index build to complete","attr":{"buildUUID":{"uuid":{"$uuid":"0f95b012-1a7d-415e-9a84-9839c759b37e"}},"deadline":{"$date":{"$numberLong":"9223372036854775807"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.577+00:00"},"s":"I", "c":"INDEX", "id":20447, "ctx":"conn12","msg":"Index build: completed","attr":{"buildUUID":{"uuid":{"$uuid":"0f95b012-1a7d-415e-9a84-9839c759b37e"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.581+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn12","msg":"createCollection","attr":{"namespace":"concelier.orchestrator_commands","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"f1a79279-2004-4cfd-8ae9-cb752e102dff"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.601+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn12","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.orchestrator_commands","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040016,"i":41}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.604+00:00"},"s":"I", "c":"INDEX", "id":20438, "ctx":"conn12","msg":"Index build: registering","attr":{"buildUUID":{"uuid":{"$uuid":"edb14da3-273d-4518-a0ff-db0a490facc4"}},"namespace":"concelier.orchestrator_commands","collectionUUID":{"uuid":{"$uuid":"f1a79279-2004-4cfd-8ae9-cb752e102dff"}},"indexes":2,"firstIndex":{"name":"orch_cmd_tenant_connector_run_seq"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.623+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn12","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.orchestrator_commands","index":"orch_cmd_tenant_connector_run_seq","commitTimestamp":{"$timestamp":{"t":1764040016,"i":43}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.623+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn12","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.orchestrator_commands","index":"orch_cmd_expiresAt_ttl","commitTimestamp":{"$timestamp":{"t":1764040016,"i":43}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.623+00:00"},"s":"I", "c":"INDEX", "id":20440, "ctx":"conn12","msg":"Index build: waiting for index build to complete","attr":{"buildUUID":{"uuid":{"$uuid":"edb14da3-273d-4518-a0ff-db0a490facc4"}},"deadline":{"$date":{"$numberLong":"9223372036854775807"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.623+00:00"},"s":"I", "c":"INDEX", "id":20447, "ctx":"conn12","msg":"Index build: completed","attr":{"buildUUID":{"uuid":{"$uuid":"edb14da3-273d-4518-a0ff-db0a490facc4"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.627+00:00"},"s":"I", "c":"STORAGE", "id":20320, "ctx":"conn12","msg":"createCollection","attr":{"namespace":"concelier.orchestrator_heartbeats","uuidDisposition":"generated","uuid":{"uuid":{"$uuid":"52b32b90-719b-4668-8aab-021f90ae99f1"}},"options":{}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.644+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn12","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.orchestrator_heartbeats","index":"_id_","commitTimestamp":{"$timestamp":{"t":1764040016,"i":44}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.648+00:00"},"s":"I", "c":"INDEX", "id":20438, "ctx":"conn12","msg":"Index build: registering","attr":{"buildUUID":{"uuid":{"$uuid":"f262f49e-88f3-4b71-ade5-24ba982d5f71"}},"namespace":"concelier.orchestrator_heartbeats","collectionUUID":{"uuid":{"$uuid":"52b32b90-719b-4668-8aab-021f90ae99f1"}},"indexes":2,"firstIndex":{"name":"orch_hb_tenant_connector_run_seq"}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.664+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn12","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.orchestrator_heartbeats","index":"orch_hb_tenant_connector_run_seq","commitTimestamp":{"$timestamp":{"t":1764040016,"i":46}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.664+00:00"},"s":"I", "c":"INDEX", "id":20345, "ctx":"conn12","msg":"Index build: done building","attr":{"buildUUID":null,"namespace":"concelier.orchestrator_heartbeats","index":"orch_hb_timestamp_desc","commitTimestamp":{"$timestamp":{"t":1764040016,"i":46}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.664+00:00"},"s":"I", "c":"INDEX", "id":20440, "ctx":"conn12","msg":"Index build: waiting for index build to complete","attr":{"buildUUID":{"uuid":{"$uuid":"f262f49e-88f3-4b71-ade5-24ba982d5f71"}},"deadline":{"$date":{"$numberLong":"9223372036854775807"}}}}
|
||||
{"t":{"$date":"2025-11-25T03:06:56.664+00:00"},"s":"I", "c":"INDEX", "id":20447, "ctx":"conn12","msg":"Index build: completed","attr":{"buildUUID":{"uuid":{"$uuid":"f262f49e-88f3-4b71-ade5-24ba982d5f71"}}}}
|
||||
info: StellaOps.Concelier.Storage.Mongo.Migrations.MongoMigrationRunner[0]
|
||||
Mongo migration 20251122_orchestrator_registry_commands applied
|
||||
info: StellaOps.Concelier.Storage.Mongo.MongoBootstrapper[0]
|
||||
Mongo bootstrapper completed
|
||||
info: MongoBootstrapper[0]
|
||||
Mongo bootstrap completed in 1453.7631 ms
|
||||
info: StellaOps.Concelier.Core.Jobs.JobSchedulerHostedService[0]
|
||||
No cron-based jobs registered; scheduler idle.
|
||||
info: StellaOps.Concelier.Storage.Mongo.Observations.AdvisoryObservationTransportWorker[0]
|
||||
Observation transport worker disabled.
|
||||
info: StellaOps.Concelier.Storage.Mongo.Observations.AdvisoryObservationTransportWorker[0]
|
||||
Observation transport worker disabled.
|
||||
info: Microsoft.Hosting.Lifetime[0]
|
||||
Application started. Press Ctrl+C to shut down.
|
||||
info: Microsoft.Hosting.Lifetime[0]
|
||||
Hosting environment: Development
|
||||
info: Microsoft.Hosting.Lifetime[0]
|
||||
Content root path: /mnt/e/dev/git.stella-ops.org/src/Concelier/StellaOps.Concelier.WebService
|
||||
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
|
||||
Request starting HTTP/1.1 GET http://localhost/health - - -
|
||||
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
|
||||
Executing endpoint 'HTTP: GET /health'
|
||||
info: Microsoft.AspNetCore.Http.Result.ContentResult[2]
|
||||
Write content with HTTP Response ContentType of application/json; charset=utf-8
|
||||
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
|
||||
Executed endpoint 'HTTP: GET /health'
|
||||
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
|
||||
Request finished HTTP/1.1 GET http://localhost/health - 200 291 application/json;+charset=utf-8 151.1386ms
|
||||
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
|
||||
Request starting HTTP/1.1 GET http://localhost/ready - - -
|
||||
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
|
||||
Executing endpoint 'HTTP: GET /ready'
|
||||
info: Microsoft.AspNetCore.Http.Result.ContentResult[2]
|
||||
Write content with HTTP Response ContentType of application/json; charset=utf-8
|
||||
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
|
||||
Executed endpoint 'HTTP: GET /ready'
|
||||
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
|
||||
Request finished HTTP/1.1 GET http://localhost/ready - 200 198 application/json;+charset=utf-8 12.4201ms
|
||||
info: Microsoft.Hosting.Lifetime[0]
|
||||
Application is shutting down...
|
||||
[xUnit.net 00:00:36.48] Finished: StellaOps.Concelier.WebService.Tests
|
||||
</StdOut>
|
||||
</Output>
|
||||
<RunInfos>
|
||||
<RunInfo computerName="DESKTOP-7GHGC2M" outcome="Warning" timestamp="2025-11-25T03:06:57.6059023+00:00">
|
||||
<Text>Data collector 'Blame' message: All tests finished running, Sequence file will not be generated.</Text>
|
||||
</RunInfo>
|
||||
</RunInfos>
|
||||
</ResultSummary>
|
||||
</TestRun>
|
||||
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TestRun id="2a53c3de-54ca-4f07-8702-1a9c0210c1e2" name="@DESKTOP-7GHGC2M 2025-11-25 03:08:54" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
|
||||
<Times creation="2025-11-25T03:08:54.4332618+00:00" queuing="2025-11-25T03:08:54.4332619+00:00" start="2025-11-25T03:08:52.3185390+00:00" finish="2025-11-25T03:08:54.4418259+00:00" />
|
||||
<TestSettings name="default" id="3ce5bdb0-5cad-4ee1-a2b9-696735144c3d">
|
||||
<Deployment runDeploymentRoot="_DESKTOP-7GHGC2M_2025-11-25_03_08_54" />
|
||||
</TestSettings>
|
||||
<Results>
|
||||
<UnitTestResult executionId="c67ce2d6-5c72-4327-ac7f-be88c6c0ecc3" testId="11684acb-bbea-2e6b-ce0d-1cc21a7bc201" testName="StellaOps.Excititor.WebService.Tests.AirgapImportEndpointTests.Import_accepts_valid_payload" computerName="DESKTOP-7GHGC2M" duration="00:00:00.0019351" startTime="2025-11-25T03:08:54.2414474+00:00" endTime="2025-11-25T03:08:54.2414476+00:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="c67ce2d6-5c72-4327-ac7f-be88c6c0ecc3" />
|
||||
<UnitTestResult executionId="21c16366-1bea-4eec-af4e-a15f4fdeb918" testId="da5d5507-70fd-de85-95fe-e405d157cf98" testName="StellaOps.Excititor.WebService.Tests.AirgapImportEndpointTests.Import_returns_bad_request_when_signature_missing" computerName="DESKTOP-7GHGC2M" duration="00:00:00.2374169" startTime="2025-11-25T03:08:54.2172539+00:00" endTime="2025-11-25T03:08:54.2173027+00:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="21c16366-1bea-4eec-af4e-a15f4fdeb918" />
|
||||
</Results>
|
||||
<TestDefinitions>
|
||||
<UnitTest name="StellaOps.Excititor.WebService.Tests.AirgapImportEndpointTests.Import_returns_bad_request_when_signature_missing" storage="/mnt/e/dev/git.stella-ops.org/src/excititor/__tests/stellaops.excititor.webservice.tests/bin/debug/net10.0/stellaops.excititor.webservice.tests.dll" id="da5d5507-70fd-de85-95fe-e405d157cf98">
|
||||
<Execution id="21c16366-1bea-4eec-af4e-a15f4fdeb918" />
|
||||
<TestMethod codeBase="/mnt/e/dev/git.stella-ops.org/src/Excititor/__Tests/StellaOps.Excititor.WebService.Tests/bin/Debug/net10.0/StellaOps.Excititor.WebService.Tests.dll" adapterTypeName="executor://xunit/VsTestRunner2/netcoreapp" className="StellaOps.Excititor.WebService.Tests.AirgapImportEndpointTests" name="Import_returns_bad_request_when_signature_missing" />
|
||||
</UnitTest>
|
||||
<UnitTest name="StellaOps.Excititor.WebService.Tests.AirgapImportEndpointTests.Import_accepts_valid_payload" storage="/mnt/e/dev/git.stella-ops.org/src/excititor/__tests/stellaops.excititor.webservice.tests/bin/debug/net10.0/stellaops.excititor.webservice.tests.dll" id="11684acb-bbea-2e6b-ce0d-1cc21a7bc201">
|
||||
<Execution id="c67ce2d6-5c72-4327-ac7f-be88c6c0ecc3" />
|
||||
<TestMethod codeBase="/mnt/e/dev/git.stella-ops.org/src/Excititor/__Tests/StellaOps.Excititor.WebService.Tests/bin/Debug/net10.0/StellaOps.Excititor.WebService.Tests.dll" adapterTypeName="executor://xunit/VsTestRunner2/netcoreapp" className="StellaOps.Excititor.WebService.Tests.AirgapImportEndpointTests" name="Import_accepts_valid_payload" />
|
||||
</UnitTest>
|
||||
</TestDefinitions>
|
||||
<TestEntries>
|
||||
<TestEntry testId="11684acb-bbea-2e6b-ce0d-1cc21a7bc201" executionId="c67ce2d6-5c72-4327-ac7f-be88c6c0ecc3" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
|
||||
<TestEntry testId="da5d5507-70fd-de85-95fe-e405d157cf98" executionId="21c16366-1bea-4eec-af4e-a15f4fdeb918" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
|
||||
</TestEntries>
|
||||
<TestLists>
|
||||
<TestList name="Results Not in a List" id="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
|
||||
<TestList name="All Loaded Results" id="19431567-8539-422a-85d7-44ee4e166bda" />
|
||||
</TestLists>
|
||||
<ResultSummary outcome="Completed">
|
||||
<Counters total="2" executed="2" passed="2" failed="0" error="0" timeout="0" aborted="0" inconclusive="0" passedButRunAborted="0" notRunnable="0" notExecuted="0" disconnected="0" warning="0" completed="0" inProgress="0" pending="0" />
|
||||
<Output>
|
||||
<StdOut>[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0-rc.2.25502.107)
|
||||
[xUnit.net 00:00:00.23] Discovering: StellaOps.Excititor.WebService.Tests
|
||||
[xUnit.net 00:00:00.29] Discovered: StellaOps.Excititor.WebService.Tests
|
||||
[xUnit.net 00:00:00.30] Starting: StellaOps.Excititor.WebService.Tests
|
||||
[xUnit.net 00:00:00.64] Finished: StellaOps.Excititor.WebService.Tests
|
||||
</StdOut>
|
||||
</Output>
|
||||
<RunInfos>
|
||||
<RunInfo computerName="DESKTOP-7GHGC2M" outcome="Warning" timestamp="2025-11-25T03:08:54.3453289+00:00">
|
||||
<Text>Data collector 'Blame' message: All tests finished running, Sequence file will not be generated.</Text>
|
||||
</RunInfo>
|
||||
</RunInfos>
|
||||
</ResultSummary>
|
||||
</TestRun>
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TestRun id="6532f9f9-e718-4fe1-b82a-33fa91310011" name="@DESKTOP-7GHGC2M 2025-11-25 03:46:20" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
|
||||
<Times creation="2025-11-25T03:46:20.2426491+00:00" queuing="2025-11-25T03:46:20.2426492+00:00" start="2025-11-25T03:46:16.0469600+00:00" finish="2025-11-25T03:46:20.2433850+00:00" />
|
||||
<TestSettings name="default" id="9a249eb2-1aba-42f8-b18c-72c4811061af">
|
||||
<Deployment runDeploymentRoot="_DESKTOP-7GHGC2M_2025-11-25_03_46_20" />
|
||||
</TestSettings>
|
||||
<TestLists>
|
||||
<TestList name="Results Not in a List" id="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
|
||||
<TestList name="All Loaded Results" id="19431567-8539-422a-85d7-44ee4e166bda" />
|
||||
</TestLists>
|
||||
<ResultSummary outcome="Completed">
|
||||
<Counters total="0" executed="0" passed="0" failed="0" error="0" timeout="0" aborted="0" inconclusive="0" passedButRunAborted="0" notRunnable="0" notExecuted="0" disconnected="0" warning="0" completed="0" inProgress="0" pending="0" />
|
||||
<Output>
|
||||
<StdOut>[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0-rc.2.25502.107)
|
||||
[xUnit.net 00:00:01.08] Discovering: StellaOps.Concelier.Storage.Mongo.Tests
|
||||
[xUnit.net 00:00:01.15] Discovered: StellaOps.Concelier.Storage.Mongo.Tests
|
||||
[xUnit.net 00:00:01.16] Starting: StellaOps.Concelier.Storage.Mongo.Tests
|
||||
[xUnit.net 00:00:01.18] Finished: StellaOps.Concelier.Storage.Mongo.Tests
|
||||
</StdOut>
|
||||
</Output>
|
||||
<RunInfos>
|
||||
<RunInfo computerName="DESKTOP-7GHGC2M" outcome="Warning" timestamp="2025-11-25T03:46:20.0884769+00:00">
|
||||
<Text>No test matches the given testcase filter `FullyQualifiedName~Orchestrator` in /mnt/e/dev/git.stella-ops.org/src/Concelier/__Tests/StellaOps.Concelier.Storage.Mongo.Tests/bin/Debug/net10.0/StellaOps.Concelier.Storage.Mongo.Tests.dll</Text>
|
||||
</RunInfo>
|
||||
<RunInfo computerName="DESKTOP-7GHGC2M" outcome="Warning" timestamp="2025-11-25T03:46:20.1812017+00:00">
|
||||
<Text>Data collector 'Blame' message: All tests finished running, Sequence file will not be generated.</Text>
|
||||
</RunInfo>
|
||||
</RunInfos>
|
||||
</ResultSummary>
|
||||
</TestRun>
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TestRun id="46df7432-fefa-4761-bcc7-948b0285de35" name="@DESKTOP-7GHGC2M 2025-11-25 06:21:32" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
|
||||
<Times creation="2025-11-25T06:21:32.4196369+00:00" queuing="2025-11-25T06:21:32.4196369+00:00" start="2025-11-25T06:21:30.6110195+00:00" finish="2025-11-25T06:21:32.4202858+00:00" />
|
||||
<TestSettings name="default" id="3f4bcc73-49aa-4ce4-bb02-f965f41971dd">
|
||||
<Deployment runDeploymentRoot="_DESKTOP-7GHGC2M_2025-11-25_06_21_32" />
|
||||
</TestSettings>
|
||||
<TestLists>
|
||||
<TestList name="Results Not in a List" id="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
|
||||
<TestList name="All Loaded Results" id="19431567-8539-422a-85d7-44ee4e166bda" />
|
||||
</TestLists>
|
||||
<ResultSummary outcome="Completed">
|
||||
<Counters total="0" executed="0" passed="0" failed="0" error="0" timeout="0" aborted="0" inconclusive="0" passedButRunAborted="0" notRunnable="0" notExecuted="0" disconnected="0" warning="0" completed="0" inProgress="0" pending="0" />
|
||||
<Output>
|
||||
<StdOut>[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0-rc.2.25502.107)
|
||||
[xUnit.net 00:00:00.26] Discovering: StellaOps.Concelier.WebService.Tests
|
||||
[xUnit.net 00:00:00.33] Discovered: StellaOps.Concelier.WebService.Tests
|
||||
[xUnit.net 00:00:00.34] Starting: StellaOps.Concelier.WebService.Tests
|
||||
[xUnit.net 00:00:00.36] Finished: StellaOps.Concelier.WebService.Tests
|
||||
</StdOut>
|
||||
</Output>
|
||||
<RunInfos>
|
||||
<RunInfo computerName="DESKTOP-7GHGC2M" outcome="Warning" timestamp="2025-11-25T06:21:32.2626160+00:00">
|
||||
<Text>No test matches the given testcase filter `ClassName~OrchestratorEndpointsTests` in /mnt/e/dev/git.stella-ops.org/src/Concelier/__Tests/StellaOps.Concelier.WebService.Tests/bin/Debug/net10.0/StellaOps.Concelier.WebService.Tests.dll</Text>
|
||||
</RunInfo>
|
||||
<RunInfo computerName="DESKTOP-7GHGC2M" outcome="Warning" timestamp="2025-11-25T06:21:32.3486581+00:00">
|
||||
<Text>Data collector 'Blame' message: All tests finished running, Sequence file will not be generated.</Text>
|
||||
</RunInfo>
|
||||
</RunInfos>
|
||||
</ResultSummary>
|
||||
</TestRun>
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TestRun id="e44b0a6b-d4da-4536-94a0-c7abc31b8451" name="@DESKTOP-7GHGC2M 2025-11-25 07:00:37" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
|
||||
<Times creation="2025-11-25T07:00:37.8701208+00:00" queuing="2025-11-25T07:00:37.8701209+00:00" start="2025-11-25T07:00:35.6229608+00:00" finish="2025-11-25T07:00:37.8707653+00:00" />
|
||||
<TestSettings name="default" id="61b86be5-caaf-4b76-a125-1141acf7e2ef">
|
||||
<Deployment runDeploymentRoot="_DESKTOP-7GHGC2M_2025-11-25_07_00_37" />
|
||||
</TestSettings>
|
||||
<TestLists>
|
||||
<TestList name="Results Not in a List" id="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
|
||||
<TestList name="All Loaded Results" id="19431567-8539-422a-85d7-44ee4e166bda" />
|
||||
</TestLists>
|
||||
<ResultSummary outcome="Completed">
|
||||
<Counters total="0" executed="0" passed="0" failed="0" error="0" timeout="0" aborted="0" inconclusive="0" passedButRunAborted="0" notRunnable="0" notExecuted="0" disconnected="0" warning="0" completed="0" inProgress="0" pending="0" />
|
||||
<Output>
|
||||
<StdOut>[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0-rc.2.25502.107)
|
||||
[xUnit.net 00:00:00.35] Discovering: StellaOps.Concelier.WebService.Tests
|
||||
[xUnit.net 00:00:00.43] Discovered: StellaOps.Concelier.WebService.Tests
|
||||
[xUnit.net 00:00:00.44] Starting: StellaOps.Concelier.WebService.Tests
|
||||
[xUnit.net 00:00:00.46] Finished: StellaOps.Concelier.WebService.Tests
|
||||
</StdOut>
|
||||
</Output>
|
||||
<RunInfos>
|
||||
<RunInfo computerName="DESKTOP-7GHGC2M" outcome="Warning" timestamp="2025-11-25T07:00:37.6968672+00:00">
|
||||
<Text>No test matches the given testcase filter `OrchestratorEndpointsTests` in /mnt/e/dev/git.stella-ops.org/src/Concelier/__Tests/StellaOps.Concelier.WebService.Tests/bin/Debug/net10.0/StellaOps.Concelier.WebService.Tests.dll</Text>
|
||||
</RunInfo>
|
||||
<RunInfo computerName="DESKTOP-7GHGC2M" outcome="Warning" timestamp="2025-11-25T07:00:37.8060888+00:00">
|
||||
<Text>Data collector 'Blame' message: All tests finished running, Sequence file will not be generated.</Text>
|
||||
</RunInfo>
|
||||
</RunInfos>
|
||||
</ResultSummary>
|
||||
</TestRun>
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"run_id": "20251207T131911Z",
|
||||
"image_tag": "stellaops/console-runner:offline-20251207T131911Z",
|
||||
"image_id": "sha256:39049b927c85ca8ae7cae79939fb36d2fa3a7ca04fb82220ef6b339b704cc0e3",
|
||||
"repo_digest": "stellaops/console-runner@sha256:39049b927c85ca8ae7cae79939fb36d2fa3a7ca04fb82220ef6b339b704cc0e3",
|
||||
"output_tar": "ops/devops/artifacts/console-runner/console-runner-20251207T131911Z.tar"
|
||||
}
|
||||
Binary file not shown.
24
devops/attestation/ALERTS.md
Normal file
24
devops/attestation/ALERTS.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Attestation Alerts & Dashboards (DEVOPS-ATTEST-75-001)
|
||||
|
||||
## Prometheus alert rules
|
||||
File: `ops/devops/attestation/attestation-alerts.yaml`
|
||||
- `AttestorSignLatencyP95High`: p95 signing latency > 2s for 5m.
|
||||
- `AttestorVerifyLatencyP95High`: p95 verification latency > 2s for 5m.
|
||||
- `AttestorVerifyFailureRate`: verification failures / requests > 2% over 5m.
|
||||
- `AttestorKeyRotationStale`: key not rotated in 30d.
|
||||
|
||||
Metrics expected:
|
||||
- `attestor_sign_duration_seconds_bucket`
|
||||
- `attestor_verify_duration_seconds_bucket`
|
||||
- `attestor_verify_failures_total`
|
||||
- `attestor_verify_requests_total`
|
||||
- `attestor_key_last_rotated_seconds` (gauge of Unix epoch seconds of last rotation)
|
||||
|
||||
## Grafana
|
||||
File: `ops/devops/attestation/grafana/attestation-latency.json`
|
||||
- Panels: signing p50/p95, verification p50/p95, failure rate, key-age gauge, last 24h error counts.
|
||||
|
||||
## Runbook
|
||||
- Verify exporters scrape `attestor-*` metrics from Attestor service.
|
||||
- Ensure alertmanager routes `team=devops` to on-call.
|
||||
- Key rotation alert: rotate via standard KMS workflow; acknowledge alert after new metric value observed.
|
||||
10
devops/attestation/README.md
Normal file
10
devops/attestation/README.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Attestor CI/Secrets (DEVOPS-ATTEST-73-001/002)
|
||||
|
||||
Artifacts added for the DevOps attestation track:
|
||||
|
||||
- `ci.yml` — GitHub Actions workflow (parity stub) that restores/builds/tests Attestor solution and uploads test artefacts. Offline/airgap friendly when mirrored into local runner; set DOTNET_* envs for determinism.
|
||||
- Secrets storage plan:
|
||||
- Use KMS-backed cosign key refs (e.g., `azurekms://...` or `awskms://...`).
|
||||
- Store ref in CI secret `ATTESTOR_COSIGN_KEY`; pipeline passes via env and never writes key material to disk.
|
||||
- Audit logs: enable KMS audit + CI job logs; avoid plaintext key dumps.
|
||||
- Next steps: wire `.gitea/workflows/attestor-ci.yml` to mirror this job, add `cosign sign-blob` stage for DSSE envelopes, and publish artefacts to `ops/devops/artifacts/attestor/<ts>/` with checksums.
|
||||
43
devops/attestation/attestation-alerts.yaml
Normal file
43
devops/attestation/attestation-alerts.yaml
Normal file
@@ -0,0 +1,43 @@
|
||||
groups:
|
||||
- name: attestor-latency
|
||||
rules:
|
||||
- alert: AttestorSignLatencyP95High
|
||||
expr: histogram_quantile(0.95, sum(rate(attestor_sign_duration_seconds_bucket[5m])) by (le)) > 2
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
team: devops
|
||||
annotations:
|
||||
summary: "Attestor signing latency p95 high"
|
||||
description: "Signing p95 is {{ $value }}s over the last 5m (threshold 2s)."
|
||||
- alert: AttestorVerifyLatencyP95High
|
||||
expr: histogram_quantile(0.95, sum(rate(attestor_verify_duration_seconds_bucket[5m])) by (le)) > 2
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
team: devops
|
||||
annotations:
|
||||
summary: "Attestor verification latency p95 high"
|
||||
description: "Verification p95 is {{ $value }}s over the last 5m (threshold 2s)."
|
||||
- name: attestor-errors
|
||||
rules:
|
||||
- alert: AttestorVerifyFailureRate
|
||||
expr: rate(attestor_verify_failures_total[5m]) / rate(attestor_verify_requests_total[5m]) > 0.02
|
||||
for: 5m
|
||||
labels:
|
||||
severity: critical
|
||||
team: devops
|
||||
annotations:
|
||||
summary: "Attestor verification failure rate above 2%"
|
||||
description: "Verification failure rate is {{ $value | humanizePercentage }} over last 5m."
|
||||
- name: attestor-keys
|
||||
rules:
|
||||
- alert: AttestorKeyRotationStale
|
||||
expr: (time() - attestor_key_last_rotated_seconds) > 60*60*24*30
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
team: devops
|
||||
annotations:
|
||||
summary: "Attestor signing key rotation overdue"
|
||||
description: "Signing key has not rotated in >30d ({{ $value }} seconds)."
|
||||
38
devops/attestation/ci.yml
Normal file
38
devops/attestation/ci.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
name: Attestor CI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- 'src/Attestor/**'
|
||||
- '.gitea/workflows/attestor-ci.yml'
|
||||
- 'ops/devops/attestation/**'
|
||||
|
||||
jobs:
|
||||
build-test:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOTNET_NOLOGO: 1
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup .NET 10
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '10.0.x'
|
||||
- name: Restore
|
||||
run: dotnet restore src/Attestor/StellaOps.Attestor.sln
|
||||
- name: Build
|
||||
run: dotnet build --no-restore -c Release src/Attestor/StellaOps.Attestor.sln
|
||||
- name: Test
|
||||
run: dotnet test --no-build -c Release src/Attestor/StellaOps.Attestor.sln
|
||||
- name: Publish artefacts
|
||||
if: always()
|
||||
run: |
|
||||
mkdir -p out/ci/attestor
|
||||
find src/Attestor -name '*.trx' -o -name '*.xml' | tar -czf out/ci/attestor/test-artifacts.tgz -T-
|
||||
- name: Upload artefacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: attestor-ci-artifacts
|
||||
path: out/ci/attestor/test-artifacts.tgz
|
||||
38
devops/attestation/grafana/attestation-latency.json
Normal file
38
devops/attestation/grafana/attestation-latency.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"title": "Attestor Latency & Errors",
|
||||
"time": { "from": "now-24h", "to": "now" },
|
||||
"panels": [
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Signing latency p50/p95",
|
||||
"targets": [
|
||||
{ "expr": "histogram_quantile(0.5, sum(rate(attestor_sign_duration_seconds_bucket[5m])) by (le))", "legendFormat": "p50" },
|
||||
{ "expr": "histogram_quantile(0.95, sum(rate(attestor_sign_duration_seconds_bucket[5m])) by (le))", "legendFormat": "p95" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Verification latency p50/p95",
|
||||
"targets": [
|
||||
{ "expr": "histogram_quantile(0.5, sum(rate(attestor_verify_duration_seconds_bucket[5m])) by (le))", "legendFormat": "p50" },
|
||||
{ "expr": "histogram_quantile(0.95, sum(rate(attestor_verify_duration_seconds_bucket[5m])) by (le))", "legendFormat": "p95" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Verification failure rate",
|
||||
"targets": [
|
||||
{ "expr": "rate(attestor_verify_failures_total[5m]) / rate(attestor_verify_requests_total[5m])", "legendFormat": "failure rate" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "stat",
|
||||
"title": "Key age (days)",
|
||||
"targets": [
|
||||
{ "expr": "(time() - attestor_key_last_rotated_seconds) / 86400" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"schemaVersion": 39,
|
||||
"version": 1
|
||||
}
|
||||
57
devops/attestation/witness-plan.md
Normal file
57
devops/attestation/witness-plan.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# Transparency Log Witness Deployment Plan (DEVOPS-ATTEST-74-001)
|
||||
|
||||
## Goals
|
||||
- Deploy and monitor a Sigstore-compatible witness for Rekor v1/v2 logs (and air-gap mirrors).
|
||||
- Provide offline-ready configs and evidence (hashes, DSSE attestations) for bootstrap packs.
|
||||
|
||||
## Scope
|
||||
- Environments: staging → prod (online), sealed/offline mirror (optional, read-only).
|
||||
- Witness duties: verify inclusion proofs, publish checkpoints/signed STHs, expose metrics and health.
|
||||
|
||||
## Architecture
|
||||
- Witness binary (sigstore/witness or equivalent) in a hardened container:
|
||||
- Non-root user, read-only rootfs, seccomp/AppArmor defaults.
|
||||
- TLS with mTLS between witness and collector; optional OIDC for admin endpoints.
|
||||
- Inputs:
|
||||
- Rekor base URL(s) + public keys.
|
||||
- Mirror CAR path + signature (for air-gap).
|
||||
- Outputs:
|
||||
- Signed checkpoints (STH) rotated hourly; stored in object storage + DSSE manifest.
|
||||
- Metrics: Prometheus `/metrics` endpoint (request latency, verify failures, checkpoint age).
|
||||
- Logs: JSON, structured, no PII.
|
||||
|
||||
## Deployment steps
|
||||
1) Build/pull witness image (pin digest); generate SBOM + cosign attestations.
|
||||
2) Create config:
|
||||
- `rekor_urls`: prod/staging
|
||||
- `rekor_keys`: PEMs
|
||||
- `checkpoint_interval`: 1h
|
||||
- `mirror_path` (optional): `/data/rekor-mirror.car`
|
||||
- `signer`: KMS ref or file key (sealed-mode uses file key from bootstrap pack)
|
||||
3) Helm/Compose template:
|
||||
- read-only rootfs, drop NET_RAW, memory/cpu limits
|
||||
- PVC for checkpoints (`/var/lib/witness/checkpoints`)
|
||||
- Service exposing HTTPS + `/metrics`
|
||||
4) CI:
|
||||
- Lint chart
|
||||
- Run e2e: start Rekor test instance, run witness, verify checkpoint written, verify metrics non-zero.
|
||||
- Publish image SBOM/attestations and chart checksums.
|
||||
5) Monitoring/alerts:
|
||||
- `witness_verify_failures_total` > 0 over 5m
|
||||
- `witness_checkpoint_age_seconds` > 5400
|
||||
- `witness_backfill_queue_depth` (if supported) above threshold
|
||||
|
||||
## Offline/air-gap mode
|
||||
- Consume signed Rekor mirror (CAR + manifest) from bootstrap pack.
|
||||
- Run witness in verify-only mode against mirror; disable outbound network.
|
||||
- Emit checkpoints signed with offline key; store in mirror bundle for audit.
|
||||
|
||||
## Evidence to capture
|
||||
- Image digest, SBOM hash, chart checksum.
|
||||
- Signed checkpoint sample and DSSE manifest.
|
||||
- CI e2e logs and metrics sample (scrape output).
|
||||
|
||||
## Owners
|
||||
- Build/deploy: DevOps Guild
|
||||
- Keys/config: Platform Security
|
||||
- Observability: Observability Guild
|
||||
136
devops/compose/README.md
Normal file
136
devops/compose/README.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# Stella Ops Compose Profiles
|
||||
|
||||
These Compose bundles ship the minimum services required to exercise the scanner pipeline plus control-plane dependencies. Every profile is pinned to immutable image digests sourced from `deploy/releases/*.yaml` and is linted via `docker compose config` in CI.
|
||||
|
||||
## Layout
|
||||
|
||||
| Path | Purpose |
|
||||
| ---- | ------- |
|
||||
| `docker-compose.dev.yaml` | Edge/nightly stack tuned for laptops and iterative work. |
|
||||
| `docker-compose.stage.yaml` | Stable channel stack mirroring pre-production clusters. |
|
||||
| `docker-compose.prod.yaml` | Production cutover stack with front-door network hand-off and Notify events enabled. |
|
||||
| `docker-compose.airgap.yaml` | Stable stack with air-gapped defaults (no outbound hostnames). |
|
||||
| `docker-compose.mirror.yaml` | Managed mirror topology for `*.stella-ops.org` distribution (Concelier + Excititor + CDN gateway). |
|
||||
| `docker-compose.telemetry.yaml` | Optional OpenTelemetry collector overlay (mutual TLS, OTLP ingest endpoints). |
|
||||
| `docker-compose.telemetry-storage.yaml` | Prometheus/Tempo/Loki storage overlay with multi-tenant defaults. |
|
||||
| `docker-compose.gpu.yaml` | Optional GPU overlay enabling NVIDIA devices for Advisory AI web/worker. Apply with `-f docker-compose.<env>.yaml -f docker-compose.gpu.yaml`. |
|
||||
| `env/*.env.example` | Seed `.env` files that document required secrets and ports per profile. |
|
||||
| `scripts/backup.sh` | Pauses workers and creates tar.gz of Mongo/MinIO/Redis volumes (deterministic snapshot). |
|
||||
| `scripts/reset.sh` | Stops the stack and removes Mongo/MinIO/Redis volumes after explicit confirmation. |
|
||||
| `scripts/quickstart.sh` | Helper to validate config and start dev stack; set `USE_MOCK=1` to include `docker-compose.mock.yaml` overlay. |
|
||||
| `docker-compose.mock.yaml` | Dev-only overlay with placeholder digests for missing services (orchestrator, policy-registry, packs, task-runner, VEX/Vuln stack). Use only with mock release manifest `deploy/releases/2025.09-mock-dev.yaml`. |
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
cp env/dev.env.example dev.env
|
||||
docker compose --env-file dev.env -f docker-compose.dev.yaml config
|
||||
docker compose --env-file dev.env -f docker-compose.dev.yaml up -d
|
||||
```
|
||||
|
||||
The stage and airgap variants behave the same way—swap the file names accordingly. All profiles expose 443/8443 for the UI and REST APIs, and they share a `stellaops` Docker network scoped to the compose project.
|
||||
|
||||
> **Surface.Secrets:** set `SCANNER_SURFACE_SECRETS_PROVIDER`/`SCANNER_SURFACE_SECRETS_ROOT` in your `.env` and point `SURFACE_SECRETS_HOST_PATH` to the decrypted bundle path (default `./offline/surface-secrets`). The stack mounts that path read-only into Scanner Web/Worker so `secret://` references resolve without embedding plaintext.
|
||||
|
||||
> **Graph Explorer reminder:** If you enable Cartographer or Graph API containers alongside these profiles, update `etc/authority.yaml` so the `cartographer-service` client is marked with `properties.serviceIdentity: "cartographer"` and carries a tenant hint. The Authority host now refuses `graph:write` tokens without that marker, so apply the configuration change before rolling out the updated images.
|
||||
|
||||
### Telemetry collector overlay
|
||||
|
||||
The OpenTelemetry collector overlay is optional and can be layered on top of any profile:
|
||||
|
||||
```bash
|
||||
./ops/devops/telemetry/generate_dev_tls.sh
|
||||
docker compose -f docker-compose.telemetry.yaml up -d
|
||||
python ../../ops/devops/telemetry/smoke_otel_collector.py --host localhost
|
||||
docker compose -f docker-compose.telemetry-storage.yaml up -d
|
||||
```
|
||||
|
||||
The generator script creates a development CA plus server/client certificates under
|
||||
`deploy/telemetry/certs/`. The smoke test sends OTLP/HTTP payloads using the generated
|
||||
client certificate and asserts the collector reports accepted traces, metrics, and logs.
|
||||
The storage overlay starts Prometheus, Tempo, and Loki with multitenancy enabled so you
|
||||
can validate the end-to-end pipeline before promoting changes to staging. Adjust the
|
||||
configs in `deploy/telemetry/storage/` before running in production.
|
||||
Mount the same certificates when running workloads so the collector can enforce mutual TLS.
|
||||
|
||||
For production cutovers copy `env/prod.env.example` to `prod.env`, update the secret placeholders, and create the external network expected by the profile:
|
||||
|
||||
```bash
|
||||
docker network create stellaops_frontdoor
|
||||
docker compose --env-file prod.env -f docker-compose.prod.yaml config
|
||||
```
|
||||
|
||||
### Scanner event stream settings
|
||||
|
||||
Scanner WebService can emit signed `scanner.report.*` events to Redis Streams when `SCANNER__EVENTS__ENABLED=true`. Each profile ships environment placeholders you can override in the `.env` file:
|
||||
|
||||
- `SCANNER_EVENTS_ENABLED` – toggle emission on/off (defaults to `false`).
|
||||
- `SCANNER_EVENTS_DRIVER` – currently only `redis` is supported.
|
||||
- `SCANNER_EVENTS_DSN` – Redis endpoint; leave blank to reuse the queue DSN when it uses `redis://`.
|
||||
- `SCANNER_EVENTS_STREAM` – stream name (`stella.events` by default).
|
||||
- `SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS` – per-publish timeout window (defaults to `5`).
|
||||
- `SCANNER_EVENTS_MAX_STREAM_LENGTH` – max stream length before Redis trims entries (defaults to `10000`).
|
||||
|
||||
Helm values mirror the same knobs under each service’s `env` map (see `deploy/helm/stellaops/values-*.yaml`).
|
||||
|
||||
### Scheduler worker configuration
|
||||
|
||||
Every Compose profile now provisions the `scheduler-worker` container (backed by the
|
||||
`StellaOps.Scheduler.Worker.Host` entrypoint). The environment placeholders exposed
|
||||
in the `.env` samples match the options bound by `AddSchedulerWorker`:
|
||||
|
||||
- `SCHEDULER_QUEUE_KIND` – queue transport (`Nats` or `Redis`).
|
||||
- `SCHEDULER_QUEUE_NATS_URL` – NATS connection string used by planner/runner consumers.
|
||||
- `SCHEDULER_STORAGE_DATABASE` – PostgreSQL database name for scheduler state.
|
||||
- `SCHEDULER_SCANNER_BASEADDRESS` – base URL the runner uses when invoking Scanner’s
|
||||
`/api/v1/reports` (defaults to the in-cluster `http://scanner-web:8444`).
|
||||
|
||||
Helm deployments inherit the same defaults from `services.scheduler-worker.env` in
|
||||
`values.yaml`; override them per environment as needed.
|
||||
|
||||
### Advisory AI configuration
|
||||
|
||||
`advisory-ai-web` hosts the API/plan cache while `advisory-ai-worker` executes queued tasks. Both containers mount the shared volumes (`advisory-ai-queue`, `advisory-ai-plans`, `advisory-ai-outputs`) so they always read/write the same deterministic state. New environment knobs:
|
||||
|
||||
- `ADVISORY_AI_SBOM_BASEADDRESS` – endpoint the SBOM context client hits (defaults to the in-cluster Scanner URL).
|
||||
- `ADVISORY_AI_INFERENCE_MODE` – `Local` (default) keeps inference on-prem; `Remote` posts sanitized prompts to the URL supplied via `ADVISORY_AI_REMOTE_BASEADDRESS`. Optional `ADVISORY_AI_REMOTE_APIKEY` carries the bearer token when remote inference is enabled.
|
||||
- `ADVISORY_AI_WEB_PORT` – host port for `advisory-ai-web`.
|
||||
|
||||
The Helm chart mirrors these settings under `services.advisory-ai-web` / `advisory-ai-worker` and expects a PVC named `stellaops-advisory-ai-data` so both deployments can mount the same RWX volume.
|
||||
|
||||
### Front-door network hand-off
|
||||
|
||||
`docker-compose.prod.yaml` adds a `frontdoor` network so operators can attach Traefik, Envoy, or an on-prem load balancer that terminates TLS. Override `FRONTDOOR_NETWORK` in `prod.env` if your reverse proxy uses a different bridge name. Attach only the externally reachable services (Authority, Signer, Attestor, Concelier, Scanner Web, Notify Web, UI) to that network—internal infrastructure (Mongo, MinIO, RustFS, NATS) stays on the private `stellaops` network.
|
||||
|
||||
### Updating to a new release
|
||||
|
||||
1. Import the new manifest into `deploy/releases/` (see `deploy/README.md`).
|
||||
2. Update image digests in the relevant Compose file(s).
|
||||
3. Re-run `docker compose config` to confirm the bundle is deterministic.
|
||||
|
||||
### Mock overlay for missing digests (dev only)
|
||||
|
||||
Until official digests land, you can exercise Compose packaging with mock placeholders:
|
||||
|
||||
```bash
|
||||
# assumes docker-compose.dev.yaml as the base profile
|
||||
USE_MOCK=1 ./scripts/quickstart.sh env/dev.env.example
|
||||
```
|
||||
|
||||
The overlay pins the missing services (orchestrator, policy-registry, packs-registry, task-runner, VEX/Vuln stack) to mock digests from `deploy/releases/2025.09-mock-dev.yaml` and starts their real entrypoints so integration flows can be exercised end-to-end. Replace the mock pins with production digests once releases publish; keep the mock overlay dev-only.
|
||||
|
||||
Keep digests synchronized between Compose, Helm, and the release manifest to preserve reproducibility guarantees. `deploy/tools/validate-profiles.sh` performs a quick audit.
|
||||
|
||||
### GPU toggle for Advisory AI
|
||||
|
||||
GPU is disabled by default. To run inference on NVIDIA GPUs:
|
||||
|
||||
```bash
|
||||
docker compose \
|
||||
--env-file prod.env \
|
||||
-f docker-compose.prod.yaml \
|
||||
-f docker-compose.gpu.yaml \
|
||||
up -d
|
||||
```
|
||||
|
||||
The GPU overlay requests one GPU for `advisory-ai-worker` and `advisory-ai-web` and sets `ADVISORY_AI_INFERENCE_GPU=true`. Ensure the host has the NVIDIA container runtime and that the base compose file still sets the correct digests.
|
||||
383
devops/compose/docker-compose.airgap.yaml
Normal file
383
devops/compose/docker-compose.airgap.yaml
Normal file
@@ -0,0 +1,383 @@
|
||||
x-release-labels: &release-labels
|
||||
com.stellaops.release.version: "2025.09.2-airgap"
|
||||
com.stellaops.release.channel: "airgap"
|
||||
com.stellaops.profile: "airgap"
|
||||
|
||||
networks:
|
||||
stellaops:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
valkey-data:
|
||||
rustfs-data:
|
||||
concelier-jobs:
|
||||
nats-data:
|
||||
scanner-surface-cache:
|
||||
postgres-data:
|
||||
advisory-ai-queue:
|
||||
advisory-ai-plans:
|
||||
advisory-ai-outputs:
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: docker.io/library/postgres:17
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: "${POSTGRES_USER:-stellaops}"
|
||||
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-stellaops}"
|
||||
POSTGRES_DB: "${POSTGRES_DB:-stellaops}"
|
||||
PGDATA: /var/lib/postgresql/data/pgdata
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
- ./postgres-init:/docker-entrypoint-initdb.d:ro
|
||||
command:
|
||||
- "postgres"
|
||||
- "-c"
|
||||
- "shared_preload_libraries=pg_stat_statements"
|
||||
- "-c"
|
||||
- "pg_stat_statements.track=all"
|
||||
ports:
|
||||
- "${POSTGRES_PORT:-25432}:5432"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
valkey:
|
||||
image: docker.io/valkey/valkey:8.0
|
||||
restart: unless-stopped
|
||||
command: ["valkey-server", "--appendonly", "yes"]
|
||||
volumes:
|
||||
- valkey-data:/data
|
||||
ports:
|
||||
- "${VALKEY_PORT:-26379}:6379"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
rustfs:
|
||||
image: registry.stella-ops.org/stellaops/rustfs:2025.10.0-edge
|
||||
command: ["serve", "--listen", "0.0.0.0:8080", "--root", "/data"]
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
RUSTFS__LOG__LEVEL: info
|
||||
RUSTFS__STORAGE__PATH: /data
|
||||
volumes:
|
||||
- rustfs-data:/data
|
||||
ports:
|
||||
- "${RUSTFS_HTTP_PORT:-8080}:8080"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
nats:
|
||||
image: docker.io/library/nats@sha256:c82559e4476289481a8a5196e675ebfe67eea81d95e5161e3e78eccfe766608e
|
||||
command:
|
||||
- "-js"
|
||||
- "-sd"
|
||||
- /data
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${NATS_CLIENT_PORT:-24222}:4222"
|
||||
volumes:
|
||||
- nats-data:/data
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
authority:
|
||||
image: registry.stella-ops.org/stellaops/authority@sha256:5551a3269b7008cd5aceecf45df018c67459ed519557ccbe48b093b926a39bcc
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- valkey
|
||||
environment:
|
||||
STELLAOPS_AUTHORITY__ISSUER: "${AUTHORITY_ISSUER}"
|
||||
STELLAOPS_AUTHORITY__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_AUTHORITY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
STELLAOPS_AUTHORITY__CACHE__REDIS__CONNECTIONSTRING: "valkey:6379"
|
||||
STELLAOPS_AUTHORITY__PLUGINDIRECTORIES__0: "/app/plugins"
|
||||
STELLAOPS_AUTHORITY__PLUGINS__CONFIGURATIONDIRECTORY: "/app/etc/authority.plugins"
|
||||
volumes:
|
||||
- ../../etc/authority.yaml:/etc/authority.yaml:ro
|
||||
- ../../etc/authority.plugins:/app/etc/authority.plugins:ro
|
||||
ports:
|
||||
- "${AUTHORITY_PORT:-8440}:8440"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
signer:
|
||||
image: registry.stella-ops.org/stellaops/signer@sha256:ddbbd664a42846cea6b40fca6465bc679b30f72851158f300d01a8571c5478fc
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- authority
|
||||
environment:
|
||||
SIGNER__AUTHORITY__BASEURL: "https://authority:8440"
|
||||
SIGNER__POE__INTROSPECTURL: "${SIGNER_POE_INTROSPECT_URL}"
|
||||
SIGNER__STORAGE__DRIVER: "postgres"
|
||||
SIGNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
ports:
|
||||
- "${SIGNER_PORT:-8441}:8441"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
attestor:
|
||||
image: registry.stella-ops.org/stellaops/attestor@sha256:1ff0a3124d66d3a2702d8e421df40fbd98cc75cb605d95510598ebbae1433c50
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- signer
|
||||
- postgres
|
||||
environment:
|
||||
ATTESTOR__SIGNER__BASEURL: "https://signer:8441"
|
||||
ATTESTOR__STORAGE__DRIVER: "postgres"
|
||||
ATTESTOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
ports:
|
||||
- "${ATTESTOR_PORT:-8442}:8442"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
issuer-directory:
|
||||
image: registry.stella-ops.org/stellaops/issuer-directory-web:2025.10.0-edge
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- authority
|
||||
environment:
|
||||
ISSUERDIRECTORY__CONFIG: "/etc/issuer-directory.yaml"
|
||||
ISSUERDIRECTORY__AUTHORITY__ISSUER: "${AUTHORITY_ISSUER}"
|
||||
ISSUERDIRECTORY__AUTHORITY__BASEURL: "https://authority:8440"
|
||||
ISSUERDIRECTORY__STORAGE__DRIVER: "postgres"
|
||||
ISSUERDIRECTORY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
ISSUERDIRECTORY__SEEDCSAFPUBLISHERS: "${ISSUER_DIRECTORY_SEED_CSAF:-true}"
|
||||
volumes:
|
||||
- ../../etc/issuer-directory.yaml:/etc/issuer-directory.yaml:ro
|
||||
ports:
|
||||
- "${ISSUER_DIRECTORY_PORT:-8447}:8080"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
concelier:
|
||||
image: registry.stella-ops.org/stellaops/concelier@sha256:29e2e1a0972707e092cbd3d370701341f9fec2aa9316fb5d8100480f2a1c76b5
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- valkey
|
||||
environment:
|
||||
CONCELIER__STORAGE__DRIVER: "postgres"
|
||||
CONCELIER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
CONCELIER__STORAGE__S3__ENDPOINT: "http://rustfs:8080"
|
||||
CONCELIER__AUTHORITY__BASEURL: "https://authority:8440"
|
||||
CONCELIER__AUTHORITY__RESILIENCE__ALLOWOFFLINECACHEFALLBACK: "true"
|
||||
CONCELIER__AUTHORITY__RESILIENCE__OFFLINECACHETOLERANCE: "${AUTHORITY_OFFLINE_CACHE_TOLERANCE:-00:30:00}"
|
||||
volumes:
|
||||
- concelier-jobs:/var/lib/concelier/jobs
|
||||
ports:
|
||||
- "${CONCELIER_PORT:-8445}:8445"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
scanner-web:
|
||||
image: registry.stella-ops.org/stellaops/scanner-web@sha256:3df8ca21878126758203c1a0444e39fd97f77ddacf04a69685cda9f1e5e94718
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- valkey
|
||||
- concelier
|
||||
- rustfs
|
||||
environment:
|
||||
SCANNER__STORAGE__DRIVER: "postgres"
|
||||
SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
SCANNER__CACHE__REDIS__CONNECTIONSTRING: "valkey:6379"
|
||||
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
|
||||
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://rustfs:8080/api/v1"
|
||||
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
|
||||
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
|
||||
SCANNER__QUEUE__BROKER: "${SCANNER_QUEUE_BROKER:-valkey://valkey:6379}"
|
||||
SCANNER__EVENTS__ENABLED: "${SCANNER_EVENTS_ENABLED:-false}"
|
||||
SCANNER__EVENTS__DRIVER: "${SCANNER_EVENTS_DRIVER:-valkey}"
|
||||
SCANNER__EVENTS__DSN: "${SCANNER_EVENTS_DSN:-}"
|
||||
SCANNER__EVENTS__STREAM: "${SCANNER_EVENTS_STREAM:-stella.events}"
|
||||
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "${SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS:-5}"
|
||||
SCANNER__EVENTS__MAXSTREAMLENGTH: "${SCANNER_EVENTS_MAX_STREAM_LENGTH:-10000}"
|
||||
SCANNER__OFFLINEKIT__ENABLED: "${SCANNER_OFFLINEKIT_ENABLED:-false}"
|
||||
SCANNER__OFFLINEKIT__REQUIREDSSE: "${SCANNER_OFFLINEKIT_REQUIREDSSE:-true}"
|
||||
SCANNER__OFFLINEKIT__REKOROFFLINEMODE: "${SCANNER_OFFLINEKIT_REKOROFFLINEMODE:-true}"
|
||||
SCANNER__OFFLINEKIT__TRUSTROOTDIRECTORY: "${SCANNER_OFFLINEKIT_TRUSTROOTDIRECTORY:-/etc/stellaops/trust-roots}"
|
||||
SCANNER__OFFLINEKIT__REKORSNAPSHOTDIRECTORY: "${SCANNER_OFFLINEKIT_REKORSNAPSHOTDIRECTORY:-/var/lib/stellaops/rekor-snapshot}"
|
||||
# Surface.Env configuration (see docs/modules/scanner/design/surface-env.md)
|
||||
SCANNER_SURFACE_FS_ENDPOINT: "${SCANNER_SURFACE_FS_ENDPOINT:-http://rustfs:8080}"
|
||||
SCANNER_SURFACE_FS_BUCKET: "${SCANNER_SURFACE_FS_BUCKET:-surface-cache}"
|
||||
SCANNER_SURFACE_CACHE_ROOT: "${SCANNER_SURFACE_CACHE_ROOT:-/var/lib/stellaops/surface}"
|
||||
SCANNER_SURFACE_CACHE_QUOTA_MB: "${SCANNER_SURFACE_CACHE_QUOTA_MB:-4096}"
|
||||
SCANNER_SURFACE_PREFETCH_ENABLED: "${SCANNER_SURFACE_PREFETCH_ENABLED:-false}"
|
||||
SCANNER_SURFACE_TENANT: "${SCANNER_SURFACE_TENANT:-default}"
|
||||
SCANNER_SURFACE_FEATURES: "${SCANNER_SURFACE_FEATURES:-}"
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER: "${SCANNER_SURFACE_SECRETS_PROVIDER:-file}"
|
||||
SCANNER_SURFACE_SECRETS_NAMESPACE: "${SCANNER_SURFACE_SECRETS_NAMESPACE:-}"
|
||||
SCANNER_SURFACE_SECRETS_ROOT: "${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}"
|
||||
SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER: "${SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER:-}"
|
||||
SCANNER_SURFACE_SECRETS_ALLOW_INLINE: "${SCANNER_SURFACE_SECRETS_ALLOW_INLINE:-false}"
|
||||
volumes:
|
||||
- scanner-surface-cache:/var/lib/stellaops/surface
|
||||
- ${SURFACE_SECRETS_HOST_PATH:-./offline/surface-secrets}:${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}:ro
|
||||
- ${SCANNER_OFFLINEKIT_TRUSTROOTS_HOST_PATH:-./offline/trust-roots}:${SCANNER_OFFLINEKIT_TRUSTROOTDIRECTORY:-/etc/stellaops/trust-roots}:ro
|
||||
- ${SCANNER_OFFLINEKIT_REKOR_SNAPSHOT_HOST_PATH:-./offline/rekor-snapshot}:${SCANNER_OFFLINEKIT_REKORSNAPSHOTDIRECTORY:-/var/lib/stellaops/rekor-snapshot}:ro
|
||||
ports:
|
||||
- "${SCANNER_WEB_PORT:-8444}:8444"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
scanner-worker:
|
||||
image: registry.stella-ops.org/stellaops/scanner-worker@sha256:eea5d6cfe7835950c5ec7a735a651f2f0d727d3e470cf9027a4a402ea89c4fb5
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- valkey
|
||||
- scanner-web
|
||||
- rustfs
|
||||
environment:
|
||||
SCANNER__STORAGE__DRIVER: "postgres"
|
||||
SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
SCANNER__CACHE__REDIS__CONNECTIONSTRING: "valkey:6379"
|
||||
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
|
||||
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://rustfs:8080/api/v1"
|
||||
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
|
||||
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
|
||||
SCANNER__QUEUE__BROKER: "${SCANNER_QUEUE_BROKER:-valkey://valkey:6379}"
|
||||
# Surface.Env configuration (see docs/modules/scanner/design/surface-env.md)
|
||||
SCANNER_SURFACE_FS_ENDPOINT: "${SCANNER_SURFACE_FS_ENDPOINT:-http://rustfs:8080}"
|
||||
SCANNER_SURFACE_FS_BUCKET: "${SCANNER_SURFACE_FS_BUCKET:-surface-cache}"
|
||||
SCANNER_SURFACE_CACHE_ROOT: "${SCANNER_SURFACE_CACHE_ROOT:-/var/lib/stellaops/surface}"
|
||||
SCANNER_SURFACE_CACHE_QUOTA_MB: "${SCANNER_SURFACE_CACHE_QUOTA_MB:-4096}"
|
||||
SCANNER_SURFACE_PREFETCH_ENABLED: "${SCANNER_SURFACE_PREFETCH_ENABLED:-false}"
|
||||
SCANNER_SURFACE_TENANT: "${SCANNER_SURFACE_TENANT:-default}"
|
||||
SCANNER_SURFACE_FEATURES: "${SCANNER_SURFACE_FEATURES:-}"
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER: "${SCANNER_SURFACE_SECRETS_PROVIDER:-file}"
|
||||
SCANNER_SURFACE_SECRETS_NAMESPACE: "${SCANNER_SURFACE_SECRETS_NAMESPACE:-}"
|
||||
SCANNER_SURFACE_SECRETS_ROOT: "${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}"
|
||||
SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER: "${SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER:-}"
|
||||
SCANNER_SURFACE_SECRETS_ALLOW_INLINE: "${SCANNER_SURFACE_SECRETS_ALLOW_INLINE:-false}"
|
||||
volumes:
|
||||
- scanner-surface-cache:/var/lib/stellaops/surface
|
||||
- ${SURFACE_SECRETS_HOST_PATH:-./offline/surface-secrets}:${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}:ro
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
scheduler-worker:
|
||||
image: registry.stella-ops.org/stellaops/scheduler-worker:2025.10.0-edge
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- valkey
|
||||
- scanner-web
|
||||
command:
|
||||
- "dotnet"
|
||||
- "StellaOps.Scheduler.Worker.Host.dll"
|
||||
environment:
|
||||
SCHEDULER__STORAGE__DRIVER: "postgres"
|
||||
SCHEDULER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
SCHEDULER__QUEUE__KIND: "${SCHEDULER_QUEUE_KIND:-Valkey}"
|
||||
SCHEDULER__QUEUE__VALKEY__URL: "${SCHEDULER_QUEUE_VALKEY_URL:-valkey:6379}"
|
||||
SCHEDULER__WORKER__RUNNER__SCANNER__BASEADDRESS: "${SCHEDULER_SCANNER_BASEADDRESS:-http://scanner-web:8444}"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
notify-web:
|
||||
image: ${NOTIFY_WEB_IMAGE:-registry.stella-ops.org/stellaops/notify-web:2025.09.2}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- authority
|
||||
environment:
|
||||
DOTNET_ENVIRONMENT: Production
|
||||
volumes:
|
||||
- ../../etc/notify.airgap.yaml:/app/etc/notify.yaml:ro
|
||||
ports:
|
||||
- "${NOTIFY_WEB_PORT:-9446}:8446"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
excititor:
|
||||
image: registry.stella-ops.org/stellaops/excititor@sha256:65c0ee13f773efe920d7181512349a09d363ab3f3e177d276136bd2742325a68
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- concelier
|
||||
environment:
|
||||
EXCITITOR__CONCELIER__BASEURL: "https://concelier:8445"
|
||||
EXCITITOR__STORAGE__DRIVER: "postgres"
|
||||
EXCITITOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
advisory-ai-web:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-web:2025.09.2-airgap
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- scanner-web
|
||||
environment:
|
||||
ADVISORYAI__AdvisoryAI__SbomBaseAddress: "${ADVISORY_AI_SBOM_BASEADDRESS:-http://scanner-web:8444}"
|
||||
ADVISORYAI__AdvisoryAI__Queue__DirectoryPath: "/var/lib/advisory-ai/queue"
|
||||
ADVISORYAI__AdvisoryAI__Storage__PlanCacheDirectory: "/var/lib/advisory-ai/plans"
|
||||
ADVISORYAI__AdvisoryAI__Storage__OutputDirectory: "/var/lib/advisory-ai/outputs"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Mode: "${ADVISORY_AI_INFERENCE_MODE:-Local}"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__BaseAddress: "${ADVISORY_AI_REMOTE_BASEADDRESS:-}"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__ApiKey: "${ADVISORY_AI_REMOTE_APIKEY:-}"
|
||||
ports:
|
||||
- "${ADVISORY_AI_WEB_PORT:-8448}:8448"
|
||||
volumes:
|
||||
- advisory-ai-queue:/var/lib/advisory-ai/queue
|
||||
- advisory-ai-plans:/var/lib/advisory-ai/plans
|
||||
- advisory-ai-outputs:/var/lib/advisory-ai/outputs
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
advisory-ai-worker:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-worker:2025.09.2-airgap
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- advisory-ai-web
|
||||
environment:
|
||||
ADVISORYAI__AdvisoryAI__SbomBaseAddress: "${ADVISORY_AI_SBOM_BASEADDRESS:-http://scanner-web:8444}"
|
||||
ADVISORYAI__AdvisoryAI__Queue__DirectoryPath: "/var/lib/advisory-ai/queue"
|
||||
ADVISORYAI__AdvisoryAI__Storage__PlanCacheDirectory: "/var/lib/advisory-ai/plans"
|
||||
ADVISORYAI__AdvisoryAI__Storage__OutputDirectory: "/var/lib/advisory-ai/outputs"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Mode: "${ADVISORY_AI_INFERENCE_MODE:-Local}"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__BaseAddress: "${ADVISORY_AI_REMOTE_BASEADDRESS:-}"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__ApiKey: "${ADVISORY_AI_REMOTE_APIKEY:-}"
|
||||
volumes:
|
||||
- advisory-ai-queue:/var/lib/advisory-ai/queue
|
||||
- advisory-ai-plans:/var/lib/advisory-ai/plans
|
||||
- advisory-ai-outputs:/var/lib/advisory-ai/outputs
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
web-ui:
|
||||
image: registry.stella-ops.org/stellaops/web-ui@sha256:bee9668011ff414572131dc777faab4da24473fe12c230893f161cabee092a1d
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- scanner-web
|
||||
environment:
|
||||
STELLAOPS_UI__BACKEND__BASEURL: "https://scanner-web:8444"
|
||||
ports:
|
||||
- "${UI_PORT:-9443}:8443"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
191
devops/compose/docker-compose.cas.yaml
Normal file
191
devops/compose/docker-compose.cas.yaml
Normal file
@@ -0,0 +1,191 @@
|
||||
# Content Addressable Storage (CAS) Infrastructure
|
||||
# Uses RustFS for S3-compatible immutable object storage
|
||||
# Aligned with best-in-class vulnerability scanner retention policies
|
||||
#
|
||||
# Usage:
|
||||
# docker compose -f docker-compose.cas.yaml up -d
|
||||
# docker compose -f docker-compose.cas.yaml -f docker-compose.dev.yaml up -d
|
||||
|
||||
x-release-labels: &release-labels
|
||||
com.stellaops.release.version: "2025.10.0-edge"
|
||||
com.stellaops.release.channel: "edge"
|
||||
com.stellaops.profile: "cas"
|
||||
|
||||
x-cas-config: &cas-config
|
||||
# Retention policies (aligned with Trivy/Grype/Anchore Enterprise)
|
||||
# - vulnerability-db: 7 days (matches Trivy default)
|
||||
# - sbom-artifacts: 365 days (audit compliance)
|
||||
# - scan-results: 90 days (SOC2/ISO27001 typical)
|
||||
# - evidence-bundles: indefinite (immutable, content-addressed)
|
||||
# - attestations: indefinite (in-toto/DSSE signed)
|
||||
CAS__RETENTION__VULNERABILITY_DB_DAYS: "7"
|
||||
CAS__RETENTION__SBOM_ARTIFACTS_DAYS: "365"
|
||||
CAS__RETENTION__SCAN_RESULTS_DAYS: "90"
|
||||
CAS__RETENTION__EVIDENCE_BUNDLES_DAYS: "0" # 0 = indefinite
|
||||
CAS__RETENTION__ATTESTATIONS_DAYS: "0" # 0 = indefinite
|
||||
CAS__RETENTION__TEMP_ARTIFACTS_DAYS: "1"
|
||||
|
||||
networks:
|
||||
cas:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
rustfs-cas-data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: ${CAS_DATA_PATH:-/var/lib/stellaops/cas}
|
||||
rustfs-evidence-data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: ${CAS_EVIDENCE_PATH:-/var/lib/stellaops/evidence}
|
||||
rustfs-attestation-data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: ${CAS_ATTESTATION_PATH:-/var/lib/stellaops/attestations}
|
||||
|
||||
services:
|
||||
# Primary CAS storage - runtime facts, signals, replay artifacts
|
||||
rustfs-cas:
|
||||
image: registry.stella-ops.org/stellaops/rustfs:2025.10.0-edge
|
||||
command: ["serve", "--listen", "0.0.0.0:8080", "--root", "/data"]
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
RUSTFS__LOG__LEVEL: "${RUSTFS_LOG_LEVEL:-info}"
|
||||
RUSTFS__STORAGE__PATH: /data
|
||||
RUSTFS__STORAGE__DEDUP: "true"
|
||||
RUSTFS__STORAGE__COMPRESSION: "${RUSTFS_COMPRESSION:-zstd}"
|
||||
RUSTFS__STORAGE__COMPRESSION_LEVEL: "${RUSTFS_COMPRESSION_LEVEL:-3}"
|
||||
# Bucket lifecycle (retention enforcement)
|
||||
RUSTFS__LIFECYCLE__ENABLED: "true"
|
||||
RUSTFS__LIFECYCLE__SCAN_INTERVAL_HOURS: "24"
|
||||
RUSTFS__LIFECYCLE__DEFAULT_RETENTION_DAYS: "90"
|
||||
# Access control
|
||||
RUSTFS__AUTH__ENABLED: "${RUSTFS_AUTH_ENABLED:-true}"
|
||||
RUSTFS__AUTH__API_KEY: "${RUSTFS_CAS_API_KEY:-cas-api-key-change-me}"
|
||||
RUSTFS__AUTH__READONLY_KEY: "${RUSTFS_CAS_READONLY_KEY:-cas-readonly-key-change-me}"
|
||||
# Service account configuration
|
||||
RUSTFS__ACCOUNTS__SCANNER__KEY: "${RUSTFS_SCANNER_KEY:-scanner-svc-key}"
|
||||
RUSTFS__ACCOUNTS__SCANNER__BUCKETS: "scanner-artifacts,surface-cache,runtime-facts"
|
||||
RUSTFS__ACCOUNTS__SCANNER__PERMISSIONS: "read,write"
|
||||
RUSTFS__ACCOUNTS__SIGNALS__KEY: "${RUSTFS_SIGNALS_KEY:-signals-svc-key}"
|
||||
RUSTFS__ACCOUNTS__SIGNALS__BUCKETS: "runtime-facts,signals-data,provenance-feed"
|
||||
RUSTFS__ACCOUNTS__SIGNALS__PERMISSIONS: "read,write"
|
||||
RUSTFS__ACCOUNTS__REPLAY__KEY: "${RUSTFS_REPLAY_KEY:-replay-svc-key}"
|
||||
RUSTFS__ACCOUNTS__REPLAY__BUCKETS: "replay-bundles,inputs-lock"
|
||||
RUSTFS__ACCOUNTS__REPLAY__PERMISSIONS: "read,write"
|
||||
RUSTFS__ACCOUNTS__READONLY__KEY: "${RUSTFS_READONLY_KEY:-readonly-svc-key}"
|
||||
RUSTFS__ACCOUNTS__READONLY__BUCKETS: "*"
|
||||
RUSTFS__ACCOUNTS__READONLY__PERMISSIONS: "read"
|
||||
<<: *cas-config
|
||||
volumes:
|
||||
- rustfs-cas-data:/data
|
||||
ports:
|
||||
- "${RUSTFS_CAS_PORT:-8180}:8080"
|
||||
networks:
|
||||
- cas
|
||||
labels: *release-labels
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
|
||||
# Evidence storage - Merkle roots, hash chains, evidence bundles (immutable)
|
||||
rustfs-evidence:
|
||||
image: registry.stella-ops.org/stellaops/rustfs:2025.10.0-edge
|
||||
command: ["serve", "--listen", "0.0.0.0:8080", "--root", "/data", "--immutable"]
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
RUSTFS__LOG__LEVEL: "${RUSTFS_LOG_LEVEL:-info}"
|
||||
RUSTFS__STORAGE__PATH: /data
|
||||
RUSTFS__STORAGE__DEDUP: "true"
|
||||
RUSTFS__STORAGE__COMPRESSION: "${RUSTFS_COMPRESSION:-zstd}"
|
||||
RUSTFS__STORAGE__IMMUTABLE: "true" # Write-once, never delete
|
||||
# Access control
|
||||
RUSTFS__AUTH__ENABLED: "true"
|
||||
RUSTFS__AUTH__API_KEY: "${RUSTFS_EVIDENCE_API_KEY:-evidence-api-key-change-me}"
|
||||
RUSTFS__AUTH__READONLY_KEY: "${RUSTFS_EVIDENCE_READONLY_KEY:-evidence-readonly-key-change-me}"
|
||||
# Service accounts
|
||||
RUSTFS__ACCOUNTS__LEDGER__KEY: "${RUSTFS_LEDGER_KEY:-ledger-svc-key}"
|
||||
RUSTFS__ACCOUNTS__LEDGER__BUCKETS: "evidence-bundles,merkle-roots,hash-chains"
|
||||
RUSTFS__ACCOUNTS__LEDGER__PERMISSIONS: "read,write"
|
||||
RUSTFS__ACCOUNTS__EXPORTER__KEY: "${RUSTFS_EXPORTER_KEY:-exporter-svc-key}"
|
||||
RUSTFS__ACCOUNTS__EXPORTER__BUCKETS: "evidence-bundles"
|
||||
RUSTFS__ACCOUNTS__EXPORTER__PERMISSIONS: "read"
|
||||
volumes:
|
||||
- rustfs-evidence-data:/data
|
||||
ports:
|
||||
- "${RUSTFS_EVIDENCE_PORT:-8181}:8080"
|
||||
networks:
|
||||
- cas
|
||||
labels: *release-labels
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
|
||||
# Attestation storage - DSSE envelopes, in-toto attestations (immutable)
|
||||
rustfs-attestation:
|
||||
image: registry.stella-ops.org/stellaops/rustfs:2025.10.0-edge
|
||||
command: ["serve", "--listen", "0.0.0.0:8080", "--root", "/data", "--immutable"]
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
RUSTFS__LOG__LEVEL: "${RUSTFS_LOG_LEVEL:-info}"
|
||||
RUSTFS__STORAGE__PATH: /data
|
||||
RUSTFS__STORAGE__DEDUP: "true"
|
||||
RUSTFS__STORAGE__COMPRESSION: "${RUSTFS_COMPRESSION:-zstd}"
|
||||
RUSTFS__STORAGE__IMMUTABLE: "true" # Write-once, never delete
|
||||
# Access control
|
||||
RUSTFS__AUTH__ENABLED: "true"
|
||||
RUSTFS__AUTH__API_KEY: "${RUSTFS_ATTESTATION_API_KEY:-attestation-api-key-change-me}"
|
||||
RUSTFS__AUTH__READONLY_KEY: "${RUSTFS_ATTESTATION_READONLY_KEY:-attestation-readonly-key-change-me}"
|
||||
# Service accounts
|
||||
RUSTFS__ACCOUNTS__ATTESTOR__KEY: "${RUSTFS_ATTESTOR_KEY:-attestor-svc-key}"
|
||||
RUSTFS__ACCOUNTS__ATTESTOR__BUCKETS: "attestations,dsse-envelopes,rekor-receipts"
|
||||
RUSTFS__ACCOUNTS__ATTESTOR__PERMISSIONS: "read,write"
|
||||
RUSTFS__ACCOUNTS__VERIFIER__KEY: "${RUSTFS_VERIFIER_KEY:-verifier-svc-key}"
|
||||
RUSTFS__ACCOUNTS__VERIFIER__BUCKETS: "attestations,dsse-envelopes,rekor-receipts"
|
||||
RUSTFS__ACCOUNTS__VERIFIER__PERMISSIONS: "read"
|
||||
volumes:
|
||||
- rustfs-attestation-data:/data
|
||||
ports:
|
||||
- "${RUSTFS_ATTESTATION_PORT:-8182}:8080"
|
||||
networks:
|
||||
- cas
|
||||
labels: *release-labels
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
|
||||
# Lifecycle manager - enforces retention policies
|
||||
cas-lifecycle:
|
||||
image: registry.stella-ops.org/stellaops/cas-lifecycle:2025.10.0-edge
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
rustfs-cas:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
LIFECYCLE__CAS__ENDPOINT: "http://rustfs-cas:8080"
|
||||
LIFECYCLE__CAS__API_KEY: "${RUSTFS_CAS_API_KEY:-cas-api-key-change-me}"
|
||||
LIFECYCLE__SCHEDULE__CRON: "${LIFECYCLE_CRON:-0 3 * * *}" # 3 AM daily
|
||||
LIFECYCLE__POLICIES__VULNERABILITY_DB: "7d"
|
||||
LIFECYCLE__POLICIES__SBOM_ARTIFACTS: "365d"
|
||||
LIFECYCLE__POLICIES__SCAN_RESULTS: "90d"
|
||||
LIFECYCLE__POLICIES__TEMP_ARTIFACTS: "1d"
|
||||
LIFECYCLE__TELEMETRY__ENABLED: "${LIFECYCLE_TELEMETRY:-true}"
|
||||
LIFECYCLE__TELEMETRY__OTLP_ENDPOINT: "${OTLP_ENDPOINT:-}"
|
||||
networks:
|
||||
- cas
|
||||
labels: *release-labels
|
||||
301
devops/compose/docker-compose.china.yml
Normal file
301
devops/compose/docker-compose.china.yml
Normal file
@@ -0,0 +1,301 @@
|
||||
# StellaOps Docker Compose - International Profile
|
||||
# Cryptography: SM2, SM3, SM4 (ShangMi / Commercial Cipher - temporarily using NIST)
|
||||
# Provider: offline-verification
|
||||
# Jurisdiction: china, world
|
||||
|
||||
x-release-labels: &release-labels
|
||||
com.stellaops.release.version: "2025.10.0-edge"
|
||||
com.stellaops.release.channel: "edge"
|
||||
com.stellaops.profile: "china"
|
||||
com.stellaops.crypto.profile: "china"
|
||||
com.stellaops.crypto.provider: "offline-verification"
|
||||
|
||||
x-crypto-env: &crypto-env
|
||||
# Crypto configuration
|
||||
STELLAOPS_CRYPTO_PROFILE: "china"
|
||||
STELLAOPS_CRYPTO_CONFIG_PATH: "/app/etc/appsettings.crypto.yaml"
|
||||
STELLAOPS_CRYPTO_MANIFEST_PATH: "/app/etc/crypto-plugins-manifest.json"
|
||||
|
||||
networks:
|
||||
stellaops:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
rustfs-data:
|
||||
concelier-jobs:
|
||||
nats-data:
|
||||
valkey-data:
|
||||
advisory-ai-queue:
|
||||
advisory-ai-plans:
|
||||
advisory-ai-outputs:
|
||||
postgres-data:
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: docker.io/library/postgres:16
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: "${POSTGRES_USER:-stellaops}"
|
||||
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-stellaops}"
|
||||
POSTGRES_DB: "${POSTGRES_DB:-stellaops_platform}"
|
||||
PGDATA: /var/lib/postgresql/data/pgdata
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
- ../postgres-partitioning:/docker-entrypoint-initdb.d:ro
|
||||
ports:
|
||||
- "${POSTGRES_PORT:-5432}:5432"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
valkey:
|
||||
image: docker.io/valkey/valkey:8.0
|
||||
restart: unless-stopped
|
||||
command: ["valkey-server", "--appendonly", "yes"]
|
||||
volumes:
|
||||
- valkey-data:/data
|
||||
ports:
|
||||
- "${VALKEY_PORT:-6379}:6379"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
rustfs:
|
||||
image: registry.stella-ops.org/stellaops/rustfs:2025.10.0-edge
|
||||
command: ["serve", "--listen", "0.0.0.0:8080", "--root", "/data"]
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
RUSTFS__LOG__LEVEL: info
|
||||
RUSTFS__STORAGE__PATH: /data
|
||||
volumes:
|
||||
- rustfs-data:/data
|
||||
ports:
|
||||
- "${RUSTFS_HTTP_PORT:-8080}:8080"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
nats:
|
||||
image: docker.io/library/nats@sha256:c82559e4476289481a8a5196e675ebfe67eea81d95e5161e3e78eccfe766608e
|
||||
command:
|
||||
- "-js"
|
||||
- "-sd"
|
||||
- /data
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${NATS_CLIENT_PORT:-4222}:4222"
|
||||
volumes:
|
||||
- nats-data:/data
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
authority:
|
||||
image: registry.stella-ops.org/stellaops/authority:china
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_AUTHORITY__ISSUER: "${AUTHORITY_ISSUER}"
|
||||
STELLAOPS_AUTHORITY__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_AUTHORITY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
STELLAOPS_AUTHORITY__PLUGINDIRECTORIES__0: "/app/plugins"
|
||||
STELLAOPS_AUTHORITY__PLUGINS__CONFIGURATIONDIRECTORY: "/app/etc/authority.plugins"
|
||||
volumes:
|
||||
- ../../etc/authority.yaml:/etc/authority.yaml:ro
|
||||
- ../../etc/authority.plugins:/app/etc/authority.plugins:ro
|
||||
- ../../etc/appsettings.crypto.china.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${AUTHORITY_PORT:-8440}:8440"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
signer:
|
||||
image: registry.stella-ops.org/stellaops/signer:china
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_SIGNER__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_SIGNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.china.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${SIGNER_PORT:-8441}:8441"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
attestor:
|
||||
image: registry.stella-ops.org/stellaops/attestor:china
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- signer
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_ATTESTOR__SIGNER__BASEURL: "http://signer:8441"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.china.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${ATTESTOR_PORT:-8442}:8442"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
concelier:
|
||||
image: registry.stella-ops.org/stellaops/concelier:china
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- rustfs
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_CONCELIER__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_CONCELIER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
STELLAOPS_CONCELIER__STORAGE__RUSTFS__BASEURL: "http://rustfs:8080"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.china.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
- concelier-jobs:/app/jobs
|
||||
ports:
|
||||
- "${CONCELIER_PORT:-8443}:8443"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
scanner:
|
||||
image: registry.stella-ops.org/stellaops/scanner:china
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_SCANNER__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.china.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${SCANNER_PORT:-8444}:8444"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
excititor:
|
||||
image: registry.stella-ops.org/stellaops/excititor:china
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_EXCITITOR__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_EXCITITOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.china.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${EXCITITOR_PORT:-8445}:8445"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
policy:
|
||||
image: registry.stella-ops.org/stellaops/policy:china
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_POLICY__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_POLICY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.china.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${POLICY_PORT:-8446}:8446"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
scheduler:
|
||||
image: registry.stella-ops.org/stellaops/scheduler:china
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- nats
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_SCHEDULER__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_SCHEDULER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
STELLAOPS_SCHEDULER__MESSAGING__NATS__URL: "nats://nats:4222"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.china.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${SCHEDULER_PORT:-8447}:8447"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
notify:
|
||||
image: registry.stella-ops.org/stellaops/notify:china
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_NOTIFY__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_NOTIFY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.china.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${NOTIFY_PORT:-8448}:8448"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
zastava:
|
||||
image: registry.stella-ops.org/stellaops/zastava:china
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_ZASTAVA__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_ZASTAVA__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.china.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${ZASTAVA_PORT:-8449}:8449"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
gateway:
|
||||
image: registry.stella-ops.org/stellaops/gateway:china
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- authority
|
||||
- concelier
|
||||
- scanner
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_GATEWAY__AUTHORITY__BASEURL: "http://authority:8440"
|
||||
STELLAOPS_GATEWAY__CONCELIER__BASEURL: "http://concelier:8443"
|
||||
STELLAOPS_GATEWAY__SCANNER__BASEURL: "http://scanner:8444"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.china.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${GATEWAY_PORT:-8080}:8080"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
353
devops/compose/docker-compose.dev.yaml
Normal file
353
devops/compose/docker-compose.dev.yaml
Normal file
@@ -0,0 +1,353 @@
|
||||
x-release-labels: &release-labels
|
||||
com.stellaops.release.version: "2025.10.0-edge"
|
||||
com.stellaops.release.channel: "edge"
|
||||
com.stellaops.profile: "dev"
|
||||
|
||||
networks:
|
||||
stellaops:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
rustfs-data:
|
||||
concelier-jobs:
|
||||
nats-data:
|
||||
valkey-data:
|
||||
advisory-ai-queue:
|
||||
advisory-ai-plans:
|
||||
advisory-ai-outputs:
|
||||
postgres-data:
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: docker.io/library/postgres:16
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: "${POSTGRES_USER:-stellaops}"
|
||||
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-stellaops}"
|
||||
POSTGRES_DB: "${POSTGRES_DB:-stellaops_platform}"
|
||||
PGDATA: /var/lib/postgresql/data/pgdata
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "${POSTGRES_PORT:-5432}:5432"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
valkey:
|
||||
image: docker.io/valkey/valkey:8.0
|
||||
restart: unless-stopped
|
||||
command: ["valkey-server", "--appendonly", "yes"]
|
||||
volumes:
|
||||
- valkey-data:/data
|
||||
ports:
|
||||
- "${VALKEY_PORT:-6379}:6379"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
rustfs:
|
||||
image: registry.stella-ops.org/stellaops/rustfs:2025.10.0-edge
|
||||
command: ["serve", "--listen", "0.0.0.0:8080", "--root", "/data"]
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
RUSTFS__LOG__LEVEL: info
|
||||
RUSTFS__STORAGE__PATH: /data
|
||||
volumes:
|
||||
- rustfs-data:/data
|
||||
ports:
|
||||
- "${RUSTFS_HTTP_PORT:-8080}:8080"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
nats:
|
||||
image: docker.io/library/nats@sha256:c82559e4476289481a8a5196e675ebfe67eea81d95e5161e3e78eccfe766608e
|
||||
command:
|
||||
- "-js"
|
||||
- "-sd"
|
||||
- /data
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${NATS_CLIENT_PORT:-4222}:4222"
|
||||
volumes:
|
||||
- nats-data:/data
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
authority:
|
||||
image: registry.stella-ops.org/stellaops/authority@sha256:a8e8faec44a579aa5714e58be835f25575710430b1ad2ccd1282a018cd9ffcdd
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
STELLAOPS_AUTHORITY__ISSUER: "${AUTHORITY_ISSUER}"
|
||||
STELLAOPS_AUTHORITY__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_AUTHORITY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
STELLAOPS_AUTHORITY__PLUGINDIRECTORIES__0: "/app/plugins"
|
||||
STELLAOPS_AUTHORITY__PLUGINS__CONFIGURATIONDIRECTORY: "/app/etc/authority.plugins"
|
||||
volumes:
|
||||
- ../../etc/authority.yaml:/etc/authority.yaml:ro
|
||||
- ../../etc/authority.plugins:/app/etc/authority.plugins:ro
|
||||
ports:
|
||||
- "${AUTHORITY_PORT:-8440}:8440"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
signer:
|
||||
image: registry.stella-ops.org/stellaops/signer@sha256:8bfef9a75783883d49fc18e3566553934e970b00ee090abee9cb110d2d5c3298
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- authority
|
||||
- valkey
|
||||
environment:
|
||||
SIGNER__AUTHORITY__BASEURL: "https://authority:8440"
|
||||
SIGNER__POE__INTROSPECTURL: "${SIGNER_POE_INTROSPECT_URL}"
|
||||
SIGNER__CACHE__REDIS__CONNECTIONSTRING: "valkey:6379"
|
||||
ports:
|
||||
- "${SIGNER_PORT:-8441}:8441"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
attestor:
|
||||
image: registry.stella-ops.org/stellaops/attestor@sha256:5cc417948c029da01dccf36e4645d961a3f6d8de7e62fe98d845f07cd2282114
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- signer
|
||||
- valkey
|
||||
environment:
|
||||
ATTESTOR__SIGNER__BASEURL: "https://signer:8441"
|
||||
ATTESTOR__CACHE__REDIS__CONNECTIONSTRING: "valkey:6379"
|
||||
ports:
|
||||
- "${ATTESTOR_PORT:-8442}:8442"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
issuer-directory:
|
||||
image: registry.stella-ops.org/stellaops/issuer-directory-web:2025.10.0-edge
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- authority
|
||||
environment:
|
||||
ISSUERDIRECTORY__CONFIG: "/etc/issuer-directory.yaml"
|
||||
ISSUERDIRECTORY__AUTHORITY__ISSUER: "${AUTHORITY_ISSUER}"
|
||||
ISSUERDIRECTORY__AUTHORITY__BASEURL: "https://authority:8440"
|
||||
ISSUERDIRECTORY__STORAGE__DRIVER: "postgres"
|
||||
ISSUERDIRECTORY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
ISSUERDIRECTORY__SEEDCSAFPUBLISHERS: "${ISSUER_DIRECTORY_SEED_CSAF:-true}"
|
||||
volumes:
|
||||
- ../../etc/issuer-directory.yaml:/etc/issuer-directory.yaml:ro
|
||||
ports:
|
||||
- "${ISSUER_DIRECTORY_PORT:-8447}:8080"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
concelier:
|
||||
image: registry.stella-ops.org/stellaops/concelier@sha256:dafef3954eb4b837e2c424dd2d23e1e4d60fa83794840fac9cd3dea1d43bd085
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
CONCELIER__STORAGE__DRIVER: "postgres"
|
||||
CONCELIER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
CONCELIER__AUTHORITY__BASEURL: "https://authority:8440"
|
||||
volumes:
|
||||
- concelier-jobs:/var/lib/concelier/jobs
|
||||
ports:
|
||||
- "${CONCELIER_PORT:-8445}:8445"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
scanner-web:
|
||||
image: registry.stella-ops.org/stellaops/scanner-web@sha256:e0dfdb087e330585a5953029fb4757f5abdf7610820a085bd61b457dbead9a11
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- concelier
|
||||
- rustfs
|
||||
- nats
|
||||
- valkey
|
||||
environment:
|
||||
SCANNER__STORAGE__DRIVER: "postgres"
|
||||
SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
|
||||
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://rustfs:8080/api/v1"
|
||||
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
|
||||
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
|
||||
SCANNER__QUEUE__BROKER: "nats://nats:4222"
|
||||
SCANNER__CACHE__REDIS__CONNECTIONSTRING: "valkey:6379"
|
||||
SCANNER__EVENTS__ENABLED: "${SCANNER_EVENTS_ENABLED:-false}"
|
||||
SCANNER__EVENTS__DRIVER: "${SCANNER_EVENTS_DRIVER:-valkey}"
|
||||
SCANNER__EVENTS__DSN: "${SCANNER_EVENTS_DSN:-valkey:6379}"
|
||||
SCANNER__EVENTS__STREAM: "${SCANNER_EVENTS_STREAM:-stella.events}"
|
||||
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "${SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS:-5}"
|
||||
SCANNER__EVENTS__MAXSTREAMLENGTH: "${SCANNER_EVENTS_MAX_STREAM_LENGTH:-10000}"
|
||||
SCANNER__OFFLINEKIT__ENABLED: "${SCANNER_OFFLINEKIT_ENABLED:-false}"
|
||||
SCANNER__OFFLINEKIT__REQUIREDSSE: "${SCANNER_OFFLINEKIT_REQUIREDSSE:-true}"
|
||||
SCANNER__OFFLINEKIT__REKOROFFLINEMODE: "${SCANNER_OFFLINEKIT_REKOROFFLINEMODE:-true}"
|
||||
SCANNER__OFFLINEKIT__TRUSTROOTDIRECTORY: "${SCANNER_OFFLINEKIT_TRUSTROOTDIRECTORY:-/etc/stellaops/trust-roots}"
|
||||
SCANNER__OFFLINEKIT__REKORSNAPSHOTDIRECTORY: "${SCANNER_OFFLINEKIT_REKORSNAPSHOTDIRECTORY:-/var/lib/stellaops/rekor-snapshot}"
|
||||
volumes:
|
||||
- ${SCANNER_OFFLINEKIT_TRUSTROOTS_HOST_PATH:-./offline/trust-roots}:${SCANNER_OFFLINEKIT_TRUSTROOTDIRECTORY:-/etc/stellaops/trust-roots}:ro
|
||||
- ${SCANNER_OFFLINEKIT_REKOR_SNAPSHOT_HOST_PATH:-./offline/rekor-snapshot}:${SCANNER_OFFLINEKIT_REKORSNAPSHOTDIRECTORY:-/var/lib/stellaops/rekor-snapshot}:ro
|
||||
ports:
|
||||
- "${SCANNER_WEB_PORT:-8444}:8444"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
scanner-worker:
|
||||
image: registry.stella-ops.org/stellaops/scanner-worker@sha256:92dda42f6f64b2d9522104a5c9ffb61d37b34dd193132b68457a259748008f37
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- scanner-web
|
||||
- rustfs
|
||||
- nats
|
||||
environment:
|
||||
SCANNER__STORAGE__DRIVER: "postgres"
|
||||
SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
|
||||
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://rustfs:8080/api/v1"
|
||||
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
|
||||
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
|
||||
SCANNER__QUEUE__BROKER: "nats://nats:4222"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
scheduler-worker:
|
||||
image: registry.stella-ops.org/stellaops/scheduler-worker:2025.10.0-edge
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- nats
|
||||
- scanner-web
|
||||
command:
|
||||
- "dotnet"
|
||||
- "StellaOps.Scheduler.Worker.Host.dll"
|
||||
environment:
|
||||
SCHEDULER__QUEUE__KIND: "Nats"
|
||||
SCHEDULER__QUEUE__NATS__URL: "nats://nats:4222"
|
||||
SCHEDULER__STORAGE__DRIVER: "postgres"
|
||||
SCHEDULER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
SCHEDULER__WORKER__RUNNER__SCANNER__BASEADDRESS: "${SCHEDULER_SCANNER_BASEADDRESS:-http://scanner-web:8444}"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
notify-web:
|
||||
image: ${NOTIFY_WEB_IMAGE:-registry.stella-ops.org/stellaops/notify-web:2025.10.0-edge}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- authority
|
||||
- valkey
|
||||
environment:
|
||||
DOTNET_ENVIRONMENT: Development
|
||||
NOTIFY__STORAGE__DRIVER: "postgres"
|
||||
NOTIFY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
NOTIFY__QUEUE__DRIVER: "nats"
|
||||
NOTIFY__QUEUE__NATS__URL: "nats://nats:4222"
|
||||
volumes:
|
||||
- ../../etc/notify.dev.yaml:/app/etc/notify.yaml:ro
|
||||
ports:
|
||||
- "${NOTIFY_WEB_PORT:-8446}:8446"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
excititor:
|
||||
image: registry.stella-ops.org/stellaops/excititor@sha256:d9bd5cadf1eab427447ce3df7302c30ded837239771cc6433b9befb895054285
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- concelier
|
||||
environment:
|
||||
EXCITITOR__CONCELIER__BASEURL: "https://concelier:8445"
|
||||
EXCITITOR__STORAGE__DRIVER: "postgres"
|
||||
EXCITITOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
advisory-ai-web:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-web:2025.10.0-edge
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- scanner-web
|
||||
environment:
|
||||
ADVISORYAI__AdvisoryAI__SbomBaseAddress: "${ADVISORY_AI_SBOM_BASEADDRESS:-http://scanner-web:8444}"
|
||||
ADVISORYAI__AdvisoryAI__Queue__DirectoryPath: "/var/lib/advisory-ai/queue"
|
||||
ADVISORYAI__AdvisoryAI__Storage__PlanCacheDirectory: "/var/lib/advisory-ai/plans"
|
||||
ADVISORYAI__AdvisoryAI__Storage__OutputDirectory: "/var/lib/advisory-ai/outputs"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Mode: "${ADVISORY_AI_INFERENCE_MODE:-Local}"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__BaseAddress: "${ADVISORY_AI_REMOTE_BASEADDRESS:-}"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__ApiKey: "${ADVISORY_AI_REMOTE_APIKEY:-}"
|
||||
ports:
|
||||
- "${ADVISORY_AI_WEB_PORT:-8448}:8448"
|
||||
volumes:
|
||||
- advisory-ai-queue:/var/lib/advisory-ai/queue
|
||||
- advisory-ai-plans:/var/lib/advisory-ai/plans
|
||||
- advisory-ai-outputs:/var/lib/advisory-ai/outputs
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
advisory-ai-worker:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-worker:2025.10.0-edge
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- advisory-ai-web
|
||||
environment:
|
||||
ADVISORYAI__AdvisoryAI__SbomBaseAddress: "${ADVISORY_AI_SBOM_BASEADDRESS:-http://scanner-web:8444}"
|
||||
ADVISORYAI__AdvisoryAI__Queue__DirectoryPath: "/var/lib/advisory-ai/queue"
|
||||
ADVISORYAI__AdvisoryAI__Storage__PlanCacheDirectory: "/var/lib/advisory-ai/plans"
|
||||
ADVISORYAI__AdvisoryAI__Storage__OutputDirectory: "/var/lib/advisory-ai/outputs"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Mode: "${ADVISORY_AI_INFERENCE_MODE:-Local}"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__BaseAddress: "${ADVISORY_AI_REMOTE_BASEADDRESS:-}"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__ApiKey: "${ADVISORY_AI_REMOTE_APIKEY:-}"
|
||||
volumes:
|
||||
- advisory-ai-queue:/var/lib/advisory-ai/queue
|
||||
- advisory-ai-plans:/var/lib/advisory-ai/plans
|
||||
- advisory-ai-outputs:/var/lib/advisory-ai/outputs
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
web-ui:
|
||||
image: registry.stella-ops.org/stellaops/web-ui@sha256:38b225fa7767a5b94ebae4dae8696044126aac429415e93de514d5dd95748dcf
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- scanner-web
|
||||
environment:
|
||||
STELLAOPS_UI__BACKEND__BASEURL: "https://scanner-web:8444"
|
||||
ports:
|
||||
- "${UI_PORT:-8443}:8443"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
cryptopro-csp:
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: ops/cryptopro/linux-csp-service/Dockerfile
|
||||
args:
|
||||
CRYPTOPRO_ACCEPT_EULA: "${CRYPTOPRO_ACCEPT_EULA:-0}"
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
ASPNETCORE_URLS: "http://0.0.0.0:8080"
|
||||
CRYPTOPRO_ACCEPT_EULA: "${CRYPTOPRO_ACCEPT_EULA:-0}"
|
||||
volumes:
|
||||
- ../../opt/cryptopro/downloads:/opt/cryptopro/downloads:ro
|
||||
ports:
|
||||
- "${CRYPTOPRO_PORT:-18080}:8080"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
301
devops/compose/docker-compose.eu.yml
Normal file
301
devops/compose/docker-compose.eu.yml
Normal file
@@ -0,0 +1,301 @@
|
||||
# StellaOps Docker Compose - International Profile
|
||||
# Cryptography: eIDAS-compliant qualified trust services (temporarily using NIST)
|
||||
# Provider: offline-verification
|
||||
# Jurisdiction: eu, world
|
||||
|
||||
x-release-labels: &release-labels
|
||||
com.stellaops.release.version: "2025.10.0-edge"
|
||||
com.stellaops.release.channel: "edge"
|
||||
com.stellaops.profile: "eu"
|
||||
com.stellaops.crypto.profile: "eu"
|
||||
com.stellaops.crypto.provider: "offline-verification"
|
||||
|
||||
x-crypto-env: &crypto-env
|
||||
# Crypto configuration
|
||||
STELLAOPS_CRYPTO_PROFILE: "eu"
|
||||
STELLAOPS_CRYPTO_CONFIG_PATH: "/app/etc/appsettings.crypto.yaml"
|
||||
STELLAOPS_CRYPTO_MANIFEST_PATH: "/app/etc/crypto-plugins-manifest.json"
|
||||
|
||||
networks:
|
||||
stellaops:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
rustfs-data:
|
||||
concelier-jobs:
|
||||
nats-data:
|
||||
valkey-data:
|
||||
advisory-ai-queue:
|
||||
advisory-ai-plans:
|
||||
advisory-ai-outputs:
|
||||
postgres-data:
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: docker.io/library/postgres:16
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: "${POSTGRES_USER:-stellaops}"
|
||||
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-stellaops}"
|
||||
POSTGRES_DB: "${POSTGRES_DB:-stellaops_platform}"
|
||||
PGDATA: /var/lib/postgresql/data/pgdata
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
- ../postgres-partitioning:/docker-entrypoint-initdb.d:ro
|
||||
ports:
|
||||
- "${POSTGRES_PORT:-5432}:5432"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
valkey:
|
||||
image: docker.io/valkey/valkey:8.0
|
||||
restart: unless-stopped
|
||||
command: ["valkey-server", "--appendonly", "yes"]
|
||||
volumes:
|
||||
- valkey-data:/data
|
||||
ports:
|
||||
- "${VALKEY_PORT:-6379}:6379"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
rustfs:
|
||||
image: registry.stella-ops.org/stellaops/rustfs:2025.10.0-edge
|
||||
command: ["serve", "--listen", "0.0.0.0:8080", "--root", "/data"]
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
RUSTFS__LOG__LEVEL: info
|
||||
RUSTFS__STORAGE__PATH: /data
|
||||
volumes:
|
||||
- rustfs-data:/data
|
||||
ports:
|
||||
- "${RUSTFS_HTTP_PORT:-8080}:8080"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
nats:
|
||||
image: docker.io/library/nats@sha256:c82559e4476289481a8a5196e675ebfe67eea81d95e5161e3e78eccfe766608e
|
||||
command:
|
||||
- "-js"
|
||||
- "-sd"
|
||||
- /data
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${NATS_CLIENT_PORT:-4222}:4222"
|
||||
volumes:
|
||||
- nats-data:/data
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
authority:
|
||||
image: registry.stella-ops.org/stellaops/authority:eu
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_AUTHORITY__ISSUER: "${AUTHORITY_ISSUER}"
|
||||
STELLAOPS_AUTHORITY__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_AUTHORITY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
STELLAOPS_AUTHORITY__PLUGINDIRECTORIES__0: "/app/plugins"
|
||||
STELLAOPS_AUTHORITY__PLUGINS__CONFIGURATIONDIRECTORY: "/app/etc/authority.plugins"
|
||||
volumes:
|
||||
- ../../etc/authority.yaml:/etc/authority.yaml:ro
|
||||
- ../../etc/authority.plugins:/app/etc/authority.plugins:ro
|
||||
- ../../etc/appsettings.crypto.eu.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${AUTHORITY_PORT:-8440}:8440"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
signer:
|
||||
image: registry.stella-ops.org/stellaops/signer:eu
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_SIGNER__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_SIGNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.eu.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${SIGNER_PORT:-8441}:8441"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
attestor:
|
||||
image: registry.stella-ops.org/stellaops/attestor:eu
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- signer
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_ATTESTOR__SIGNER__BASEURL: "http://signer:8441"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.eu.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${ATTESTOR_PORT:-8442}:8442"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
concelier:
|
||||
image: registry.stella-ops.org/stellaops/concelier:eu
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- rustfs
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_CONCELIER__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_CONCELIER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
STELLAOPS_CONCELIER__STORAGE__RUSTFS__BASEURL: "http://rustfs:8080"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.eu.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
- concelier-jobs:/app/jobs
|
||||
ports:
|
||||
- "${CONCELIER_PORT:-8443}:8443"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
scanner:
|
||||
image: registry.stella-ops.org/stellaops/scanner:eu
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_SCANNER__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.eu.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${SCANNER_PORT:-8444}:8444"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
excititor:
|
||||
image: registry.stella-ops.org/stellaops/excititor:eu
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_EXCITITOR__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_EXCITITOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.eu.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${EXCITITOR_PORT:-8445}:8445"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
policy:
|
||||
image: registry.stella-ops.org/stellaops/policy:eu
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_POLICY__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_POLICY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.eu.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${POLICY_PORT:-8446}:8446"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
scheduler:
|
||||
image: registry.stella-ops.org/stellaops/scheduler:eu
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- nats
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_SCHEDULER__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_SCHEDULER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
STELLAOPS_SCHEDULER__MESSAGING__NATS__URL: "nats://nats:4222"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.eu.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${SCHEDULER_PORT:-8447}:8447"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
notify:
|
||||
image: registry.stella-ops.org/stellaops/notify:eu
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_NOTIFY__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_NOTIFY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.eu.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${NOTIFY_PORT:-8448}:8448"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
zastava:
|
||||
image: registry.stella-ops.org/stellaops/zastava:eu
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_ZASTAVA__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_ZASTAVA__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.eu.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${ZASTAVA_PORT:-8449}:8449"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
gateway:
|
||||
image: registry.stella-ops.org/stellaops/gateway:eu
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- authority
|
||||
- concelier
|
||||
- scanner
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_GATEWAY__AUTHORITY__BASEURL: "http://authority:8440"
|
||||
STELLAOPS_GATEWAY__CONCELIER__BASEURL: "http://concelier:8443"
|
||||
STELLAOPS_GATEWAY__SCANNER__BASEURL: "http://scanner:8444"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.eu.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${GATEWAY_PORT:-8080}:8080"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
26
devops/compose/docker-compose.gpu.yaml
Normal file
26
devops/compose/docker-compose.gpu.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
advisory-ai-worker:
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- capabilities: [gpu]
|
||||
driver: nvidia
|
||||
count: 1
|
||||
environment:
|
||||
ADVISORY_AI_INFERENCE_GPU: "true"
|
||||
runtime: nvidia
|
||||
|
||||
advisory-ai-web:
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- capabilities: [gpu]
|
||||
driver: nvidia
|
||||
count: 1
|
||||
environment:
|
||||
ADVISORY_AI_INFERENCE_GPU: "true"
|
||||
runtime: nvidia
|
||||
301
devops/compose/docker-compose.international.yml
Normal file
301
devops/compose/docker-compose.international.yml
Normal file
@@ -0,0 +1,301 @@
|
||||
# StellaOps Docker Compose - International Profile
|
||||
# Cryptography: Standard NIST algorithms (ECDSA, RSA, SHA-2)
|
||||
# Provider: offline-verification
|
||||
# Jurisdiction: world
|
||||
|
||||
x-release-labels: &release-labels
|
||||
com.stellaops.release.version: "2025.10.0-edge"
|
||||
com.stellaops.release.channel: "edge"
|
||||
com.stellaops.profile: "international"
|
||||
com.stellaops.crypto.profile: "international"
|
||||
com.stellaops.crypto.provider: "offline-verification"
|
||||
|
||||
x-crypto-env: &crypto-env
|
||||
# Crypto configuration
|
||||
STELLAOPS_CRYPTO_PROFILE: "international"
|
||||
STELLAOPS_CRYPTO_CONFIG_PATH: "/app/etc/appsettings.crypto.yaml"
|
||||
STELLAOPS_CRYPTO_MANIFEST_PATH: "/app/etc/crypto-plugins-manifest.json"
|
||||
|
||||
networks:
|
||||
stellaops:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
rustfs-data:
|
||||
concelier-jobs:
|
||||
nats-data:
|
||||
valkey-data:
|
||||
advisory-ai-queue:
|
||||
advisory-ai-plans:
|
||||
advisory-ai-outputs:
|
||||
postgres-data:
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: docker.io/library/postgres:16
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: "${POSTGRES_USER:-stellaops}"
|
||||
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-stellaops}"
|
||||
POSTGRES_DB: "${POSTGRES_DB:-stellaops_platform}"
|
||||
PGDATA: /var/lib/postgresql/data/pgdata
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
- ../postgres-partitioning:/docker-entrypoint-initdb.d:ro
|
||||
ports:
|
||||
- "${POSTGRES_PORT:-5432}:5432"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
valkey:
|
||||
image: docker.io/valkey/valkey:8.0
|
||||
restart: unless-stopped
|
||||
command: ["valkey-server", "--appendonly", "yes"]
|
||||
volumes:
|
||||
- valkey-data:/data
|
||||
ports:
|
||||
- "${VALKEY_PORT:-6379}:6379"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
rustfs:
|
||||
image: registry.stella-ops.org/stellaops/rustfs:2025.10.0-edge
|
||||
command: ["serve", "--listen", "0.0.0.0:8080", "--root", "/data"]
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
RUSTFS__LOG__LEVEL: info
|
||||
RUSTFS__STORAGE__PATH: /data
|
||||
volumes:
|
||||
- rustfs-data:/data
|
||||
ports:
|
||||
- "${RUSTFS_HTTP_PORT:-8080}:8080"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
nats:
|
||||
image: docker.io/library/nats@sha256:c82559e4476289481a8a5196e675ebfe67eea81d95e5161e3e78eccfe766608e
|
||||
command:
|
||||
- "-js"
|
||||
- "-sd"
|
||||
- /data
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${NATS_CLIENT_PORT:-4222}:4222"
|
||||
volumes:
|
||||
- nats-data:/data
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
authority:
|
||||
image: registry.stella-ops.org/stellaops/authority:international
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_AUTHORITY__ISSUER: "${AUTHORITY_ISSUER}"
|
||||
STELLAOPS_AUTHORITY__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_AUTHORITY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
STELLAOPS_AUTHORITY__PLUGINDIRECTORIES__0: "/app/plugins"
|
||||
STELLAOPS_AUTHORITY__PLUGINS__CONFIGURATIONDIRECTORY: "/app/etc/authority.plugins"
|
||||
volumes:
|
||||
- ../../etc/authority.yaml:/etc/authority.yaml:ro
|
||||
- ../../etc/authority.plugins:/app/etc/authority.plugins:ro
|
||||
- ../../etc/appsettings.crypto.international.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${AUTHORITY_PORT:-8440}:8440"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
signer:
|
||||
image: registry.stella-ops.org/stellaops/signer:international
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_SIGNER__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_SIGNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.international.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${SIGNER_PORT:-8441}:8441"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
attestor:
|
||||
image: registry.stella-ops.org/stellaops/attestor:international
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- signer
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_ATTESTOR__SIGNER__BASEURL: "http://signer:8441"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.international.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${ATTESTOR_PORT:-8442}:8442"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
concelier:
|
||||
image: registry.stella-ops.org/stellaops/concelier:international
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- rustfs
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_CONCELIER__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_CONCELIER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
STELLAOPS_CONCELIER__STORAGE__RUSTFS__BASEURL: "http://rustfs:8080"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.international.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
- concelier-jobs:/app/jobs
|
||||
ports:
|
||||
- "${CONCELIER_PORT:-8443}:8443"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
scanner:
|
||||
image: registry.stella-ops.org/stellaops/scanner:international
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_SCANNER__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.international.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${SCANNER_PORT:-8444}:8444"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
excititor:
|
||||
image: registry.stella-ops.org/stellaops/excititor:international
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_EXCITITOR__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_EXCITITOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.international.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${EXCITITOR_PORT:-8445}:8445"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
policy:
|
||||
image: registry.stella-ops.org/stellaops/policy:international
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_POLICY__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_POLICY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.international.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${POLICY_PORT:-8446}:8446"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
scheduler:
|
||||
image: registry.stella-ops.org/stellaops/scheduler:international
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- nats
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_SCHEDULER__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_SCHEDULER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
STELLAOPS_SCHEDULER__MESSAGING__NATS__URL: "nats://nats:4222"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.international.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${SCHEDULER_PORT:-8447}:8447"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
notify:
|
||||
image: registry.stella-ops.org/stellaops/notify:international
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_NOTIFY__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_NOTIFY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.international.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${NOTIFY_PORT:-8448}:8448"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
zastava:
|
||||
image: registry.stella-ops.org/stellaops/zastava:international
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_ZASTAVA__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_ZASTAVA__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.international.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${ZASTAVA_PORT:-8449}:8449"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
gateway:
|
||||
image: registry.stella-ops.org/stellaops/gateway:international
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- authority
|
||||
- concelier
|
||||
- scanner
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_GATEWAY__AUTHORITY__BASEURL: "http://authority:8440"
|
||||
STELLAOPS_GATEWAY__CONCELIER__BASEURL: "http://concelier:8443"
|
||||
STELLAOPS_GATEWAY__SCANNER__BASEURL: "http://scanner:8444"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.international.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${GATEWAY_PORT:-8080}:8080"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
152
devops/compose/docker-compose.mirror.yaml
Normal file
152
devops/compose/docker-compose.mirror.yaml
Normal file
@@ -0,0 +1,152 @@
|
||||
x-release-labels: &release-labels
|
||||
com.stellaops.release.version: "2025.10.0-edge"
|
||||
com.stellaops.release.channel: "edge"
|
||||
com.stellaops.profile: "mirror-managed"
|
||||
|
||||
networks:
|
||||
mirror:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
mongo-data:
|
||||
minio-data:
|
||||
concelier-jobs:
|
||||
concelier-exports:
|
||||
excititor-exports:
|
||||
nginx-cache:
|
||||
|
||||
services:
|
||||
mongo:
|
||||
image: docker.io/library/mongo@sha256:c258b26dbb7774f97f52aff52231ca5f228273a84329c5f5e451c3739457db49
|
||||
command: ["mongod", "--bind_ip_all"]
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MONGO_INITDB_ROOT_USERNAME: "${MONGO_INITDB_ROOT_USERNAME:-stellaops_mirror}"
|
||||
MONGO_INITDB_ROOT_PASSWORD: "${MONGO_INITDB_ROOT_PASSWORD:-mirror-password}"
|
||||
volumes:
|
||||
- mongo-data:/data/db
|
||||
networks:
|
||||
- mirror
|
||||
labels: *release-labels
|
||||
|
||||
minio:
|
||||
image: docker.io/minio/minio@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e
|
||||
command: ["server", "/data", "--console-address", ":9001"]
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MINIO_ROOT_USER: "${MINIO_ROOT_USER:-stellaops-mirror}"
|
||||
MINIO_ROOT_PASSWORD: "${MINIO_ROOT_PASSWORD:-mirror-minio-secret}"
|
||||
volumes:
|
||||
- minio-data:/data
|
||||
networks:
|
||||
- mirror
|
||||
labels: *release-labels
|
||||
|
||||
concelier:
|
||||
image: registry.stella-ops.org/stellaops/concelier@sha256:dafef3954eb4b837e2c424dd2d23e1e4d60fa83794840fac9cd3dea1d43bd085
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- mongo
|
||||
- minio
|
||||
environment:
|
||||
ASPNETCORE_URLS: "http://+:8445"
|
||||
CONCELIER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://${MONGO_INITDB_ROOT_USERNAME:-stellaops_mirror}:${MONGO_INITDB_ROOT_PASSWORD:-mirror-password}@mongo:27017/concelier?authSource=admin"
|
||||
CONCELIER__STORAGE__S3__ENDPOINT: "http://minio:9000"
|
||||
CONCELIER__STORAGE__S3__ACCESSKEYID: "${MINIO_ROOT_USER:-stellaops-mirror}"
|
||||
CONCELIER__STORAGE__S3__SECRETACCESSKEY: "${MINIO_ROOT_PASSWORD:-mirror-minio-secret}"
|
||||
CONCELIER__TELEMETRY__SERVICENAME: "stellaops-concelier-mirror"
|
||||
CONCELIER__MIRROR__ENABLED: "true"
|
||||
CONCELIER__MIRROR__EXPORTROOT: "/exports/json"
|
||||
CONCELIER__MIRROR__LATESTDIRECTORYNAME: "${CONCELIER_MIRROR_LATEST_SEGMENT:-latest}"
|
||||
CONCELIER__MIRROR__MIRRORDIRECTORYNAME: "${CONCELIER_MIRROR_DIRECTORY_SEGMENT:-mirror}"
|
||||
CONCELIER__MIRROR__REQUIREAUTHENTICATION: "${CONCELIER_MIRROR_REQUIRE_AUTH:-true}"
|
||||
CONCELIER__MIRROR__MAXINDEXREQUESTSPERHOUR: "${CONCELIER_MIRROR_INDEX_BUDGET:-600}"
|
||||
CONCELIER__MIRROR__DOMAINS__0__ID: "${CONCELIER_MIRROR_DOMAIN_PRIMARY_ID:-primary}"
|
||||
CONCELIER__MIRROR__DOMAINS__0__DISPLAYNAME: "${CONCELIER_MIRROR_DOMAIN_PRIMARY_NAME:-Primary Mirror}"
|
||||
CONCELIER__MIRROR__DOMAINS__0__REQUIREAUTHENTICATION: "${CONCELIER_MIRROR_DOMAIN_PRIMARY_AUTH:-true}"
|
||||
CONCELIER__MIRROR__DOMAINS__0__MAXDOWNLOADREQUESTSPERHOUR: "${CONCELIER_MIRROR_DOMAIN_PRIMARY_DOWNLOAD_BUDGET:-3600}"
|
||||
CONCELIER__MIRROR__DOMAINS__1__ID: "${CONCELIER_MIRROR_DOMAIN_SECONDARY_ID:-community}"
|
||||
CONCELIER__MIRROR__DOMAINS__1__DISPLAYNAME: "${CONCELIER_MIRROR_DOMAIN_SECONDARY_NAME:-Community Mirror}"
|
||||
CONCELIER__MIRROR__DOMAINS__1__REQUIREAUTHENTICATION: "${CONCELIER_MIRROR_DOMAIN_SECONDARY_AUTH:-false}"
|
||||
CONCELIER__MIRROR__DOMAINS__1__MAXDOWNLOADREQUESTSPERHOUR: "${CONCELIER_MIRROR_DOMAIN_SECONDARY_DOWNLOAD_BUDGET:-1800}"
|
||||
CONCELIER__AUTHORITY__ENABLED: "${CONCELIER_AUTHORITY_ENABLED:-true}"
|
||||
CONCELIER__AUTHORITY__ALLOWANONYMOUSFALLBACK: "${CONCELIER_AUTHORITY_ALLOW_ANON:-false}"
|
||||
CONCELIER__AUTHORITY__ISSUER: "${CONCELIER_AUTHORITY_ISSUER:-https://authority.stella-ops.org}"
|
||||
CONCELIER__AUTHORITY__METADATAADDRESS: "${CONCELIER_AUTHORITY_METADATA:-}"
|
||||
CONCELIER__AUTHORITY__CLIENTID: "${CONCELIER_AUTHORITY_CLIENT_ID:-stellaops-concelier-mirror}"
|
||||
CONCELIER__AUTHORITY__CLIENTSECRETFILE: "/run/secrets/concelier-authority-client"
|
||||
CONCELIER__AUTHORITY__CLIENTSCOPES__0: "${CONCELIER_AUTHORITY_SCOPE:-concelier.mirror.read}"
|
||||
CONCELIER__AUTHORITY__AUDIENCES__0: "${CONCELIER_AUTHORITY_AUDIENCE:-api://concelier.mirror}"
|
||||
CONCELIER__AUTHORITY__BYPASSNETWORKS__0: "10.0.0.0/8"
|
||||
CONCELIER__AUTHORITY__BYPASSNETWORKS__1: "127.0.0.1/32"
|
||||
CONCELIER__AUTHORITY__BYPASSNETWORKS__2: "::1/128"
|
||||
CONCELIER__AUTHORITY__RESILIENCE__ENABLERETRIES: "true"
|
||||
CONCELIER__AUTHORITY__RESILIENCE__RETRYDELAYS__0: "00:00:01"
|
||||
CONCELIER__AUTHORITY__RESILIENCE__RETRYDELAYS__1: "00:00:02"
|
||||
CONCELIER__AUTHORITY__RESILIENCE__RETRYDELAYS__2: "00:00:05"
|
||||
CONCELIER__AUTHORITY__RESILIENCE__ALLOWOFFLINECACHEFALLBACK: "true"
|
||||
CONCELIER__AUTHORITY__RESILIENCE__OFFLINECACHETOLERANCE: "00:10:00"
|
||||
volumes:
|
||||
- concelier-jobs:/var/lib/concelier/jobs
|
||||
- concelier-exports:/exports/json
|
||||
- ./mirror-secrets:/run/secrets:ro
|
||||
networks:
|
||||
- mirror
|
||||
labels: *release-labels
|
||||
|
||||
excititor:
|
||||
image: registry.stella-ops.org/stellaops/excititor@sha256:d9bd5cadf1eab427447ce3df7302c30ded837239771cc6433b9befb895054285
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- mongo
|
||||
environment:
|
||||
ASPNETCORE_URLS: "http://+:8448"
|
||||
EXCITITOR__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://${MONGO_INITDB_ROOT_USERNAME:-stellaops_mirror}:${MONGO_INITDB_ROOT_PASSWORD:-mirror-password}@mongo:27017/excititor?authSource=admin"
|
||||
EXCITITOR__STORAGE__MONGO__DATABASENAME: "${EXCITITOR_MONGO_DATABASE:-excititor}"
|
||||
EXCITITOR__ARTIFACTS__FILESYSTEM__ROOT: "/exports"
|
||||
EXCITITOR__ARTIFACTS__FILESYSTEM__OVERWRITEEXISTING: "${EXCITITOR_FILESYSTEM_OVERWRITE:-false}"
|
||||
EXCITITOR__MIRROR__DOMAINS__0__ID: "${EXCITITOR_MIRROR_DOMAIN_PRIMARY_ID:-primary}"
|
||||
EXCITITOR__MIRROR__DOMAINS__0__DISPLAYNAME: "${EXCITITOR_MIRROR_DOMAIN_PRIMARY_NAME:-Primary Mirror}"
|
||||
EXCITITOR__MIRROR__DOMAINS__0__REQUIREAUTHENTICATION: "${EXCITITOR_MIRROR_DOMAIN_PRIMARY_AUTH:-true}"
|
||||
EXCITITOR__MIRROR__DOMAINS__0__MAXINDEXREQUESTSPERHOUR: "${EXCITITOR_MIRROR_DOMAIN_PRIMARY_INDEX_BUDGET:-300}"
|
||||
EXCITITOR__MIRROR__DOMAINS__0__MAXDOWNLOADREQUESTSPERHOUR: "${EXCITITOR_MIRROR_DOMAIN_PRIMARY_DOWNLOAD_BUDGET:-2400}"
|
||||
EXCITITOR__MIRROR__DOMAINS__0__EXPORTS__0__KEY: "${EXCITITOR_MIRROR_PRIMARY_EXPORT_CONSENSUS_KEY:-consensus-json}"
|
||||
EXCITITOR__MIRROR__DOMAINS__0__EXPORTS__0__FORMAT: "${EXCITITOR_MIRROR_PRIMARY_EXPORT_CONSENSUS_FORMAT:-json}"
|
||||
EXCITITOR__MIRROR__DOMAINS__0__EXPORTS__0__VIEW: "${EXCITITOR_MIRROR_PRIMARY_EXPORT_CONSENSUS_VIEW:-consensus}"
|
||||
EXCITITOR__MIRROR__DOMAINS__0__EXPORTS__1__KEY: "${EXCITITOR_MIRROR_PRIMARY_EXPORT_OPENVEX_KEY:-consensus-openvex}"
|
||||
EXCITITOR__MIRROR__DOMAINS__0__EXPORTS__1__FORMAT: "${EXCITITOR_MIRROR_PRIMARY_EXPORT_OPENVEX_FORMAT:-openvex}"
|
||||
EXCITITOR__MIRROR__DOMAINS__0__EXPORTS__1__VIEW: "${EXCITITOR_MIRROR_PRIMARY_EXPORT_OPENVEX_VIEW:-consensus}"
|
||||
EXCITITOR__MIRROR__DOMAINS__1__ID: "${EXCITITOR_MIRROR_DOMAIN_SECONDARY_ID:-community}"
|
||||
EXCITITOR__MIRROR__DOMAINS__1__DISPLAYNAME: "${EXCITITOR_MIRROR_DOMAIN_SECONDARY_NAME:-Community Mirror}"
|
||||
EXCITITOR__MIRROR__DOMAINS__1__REQUIREAUTHENTICATION: "${EXCITITOR_MIRROR_DOMAIN_SECONDARY_AUTH:-false}"
|
||||
EXCITITOR__MIRROR__DOMAINS__1__MAXINDEXREQUESTSPERHOUR: "${EXCITITOR_MIRROR_DOMAIN_SECONDARY_INDEX_BUDGET:-120}"
|
||||
EXCITITOR__MIRROR__DOMAINS__1__MAXDOWNLOADREQUESTSPERHOUR: "${EXCITITOR_MIRROR_DOMAIN_SECONDARY_DOWNLOAD_BUDGET:-600}"
|
||||
EXCITITOR__MIRROR__DOMAINS__1__EXPORTS__0__KEY: "${EXCITITOR_MIRROR_SECONDARY_EXPORT_KEY:-community-consensus}"
|
||||
EXCITITOR__MIRROR__DOMAINS__1__EXPORTS__0__FORMAT: "${EXCITITOR_MIRROR_SECONDARY_EXPORT_FORMAT:-json}"
|
||||
EXCITITOR__MIRROR__DOMAINS__1__EXPORTS__0__VIEW: "${EXCITITOR_MIRROR_SECONDARY_EXPORT_VIEW:-consensus}"
|
||||
volumes:
|
||||
- excititor-exports:/exports
|
||||
- ./mirror-secrets:/run/secrets:ro
|
||||
expose:
|
||||
- "8448"
|
||||
networks:
|
||||
- mirror
|
||||
labels: *release-labels
|
||||
|
||||
mirror-gateway:
|
||||
image: docker.io/library/nginx@sha256:208b70eefac13ee9be00e486f79c695b15cef861c680527171a27d253d834be9
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- concelier
|
||||
- excititor
|
||||
ports:
|
||||
- "${MIRROR_GATEWAY_HTTP_PORT:-8080}:80"
|
||||
- "${MIRROR_GATEWAY_HTTPS_PORT:-9443}:443"
|
||||
volumes:
|
||||
- nginx-cache:/var/cache/nginx
|
||||
- ./mirror-gateway/conf.d:/etc/nginx/conf.d:ro
|
||||
- ./mirror-gateway/tls:/etc/nginx/tls:ro
|
||||
- ./mirror-gateway/secrets:/etc/nginx/secrets:ro
|
||||
networks:
|
||||
- mirror
|
||||
labels: *release-labels
|
||||
90
devops/compose/docker-compose.mock.yaml
Normal file
90
devops/compose/docker-compose.mock.yaml
Normal file
@@ -0,0 +1,90 @@
|
||||
x-release-labels: &release-labels
|
||||
com.stellaops.release.version: "2025.09.2-mock"
|
||||
com.stellaops.release.channel: "dev-mock"
|
||||
com.stellaops.profile: "mock-overlay"
|
||||
|
||||
services:
|
||||
orchestrator:
|
||||
image: registry.stella-ops.org/stellaops/orchestrator@sha256:97f12856ce870bafd3328bda86833bcccbf56d255941d804966b5557f6610119
|
||||
command: ["dotnet", "StellaOps.Orchestrator.WebService.dll"]
|
||||
depends_on:
|
||||
- mongo
|
||||
- nats
|
||||
labels: *release-labels
|
||||
networks: [stellaops]
|
||||
|
||||
policy-registry:
|
||||
image: registry.stella-ops.org/stellaops/policy-registry@sha256:c6cad8055e9827ebcbebb6ad4d6866dce4b83a0a49b0a8a6500b736a5cb26fa7
|
||||
command: ["dotnet", "StellaOps.Policy.Engine.dll"]
|
||||
depends_on:
|
||||
- mongo
|
||||
labels: *release-labels
|
||||
networks: [stellaops]
|
||||
|
||||
vex-lens:
|
||||
image: registry.stella-ops.org/stellaops/vex-lens@sha256:b44e63ecfeebc345a70c073c1ce5ace709c58be0ffaad0e2862758aeee3092fb
|
||||
command: ["dotnet", "StellaOps.VexLens.dll"]
|
||||
depends_on:
|
||||
- mongo
|
||||
labels: *release-labels
|
||||
networks: [stellaops]
|
||||
|
||||
issuer-directory:
|
||||
image: registry.stella-ops.org/stellaops/issuer-directory@sha256:67e8ef02c97d3156741e857756994888f30c373ace8e84886762edba9dc51914
|
||||
command: ["dotnet", "StellaOps.IssuerDirectory.Web.dll"]
|
||||
depends_on:
|
||||
- mongo
|
||||
- authority
|
||||
labels: *release-labels
|
||||
networks: [stellaops]
|
||||
|
||||
findings-ledger:
|
||||
image: registry.stella-ops.org/stellaops/findings-ledger@sha256:71d4c361ba8b2f8b69d652597bc3f2efc8a64f93fab854ce25272a88506df49c
|
||||
command: ["dotnet", "StellaOps.Findings.Ledger.WebService.dll"]
|
||||
depends_on:
|
||||
- postgres
|
||||
- authority
|
||||
labels: *release-labels
|
||||
networks: [stellaops]
|
||||
|
||||
vuln-explorer-api:
|
||||
image: registry.stella-ops.org/stellaops/vuln-explorer-api@sha256:7fc7e43a05cbeb0106ce7d4d634612e83de6fdc119aaab754a71c1d60b82841d
|
||||
command: ["dotnet", "StellaOps.VulnExplorer.Api.dll"]
|
||||
depends_on:
|
||||
- findings-ledger
|
||||
- authority
|
||||
labels: *release-labels
|
||||
networks: [stellaops]
|
||||
|
||||
packs-registry:
|
||||
image: registry.stella-ops.org/stellaops/packs-registry@sha256:1f5e9416c4dc608594ad6fad87c24d72134427f899c192b494e22b268499c791
|
||||
command: ["dotnet", "StellaOps.PacksRegistry.dll"]
|
||||
depends_on:
|
||||
- mongo
|
||||
labels: *release-labels
|
||||
networks: [stellaops]
|
||||
|
||||
task-runner:
|
||||
image: registry.stella-ops.org/stellaops/task-runner@sha256:eb5ad992b49a41554f41516be1a6afcfa6522faf2111c08ff2b3664ad2fc954b
|
||||
command: ["dotnet", "StellaOps.TaskRunner.WebService.dll"]
|
||||
depends_on:
|
||||
- packs-registry
|
||||
- postgres
|
||||
labels: *release-labels
|
||||
networks: [stellaops]
|
||||
|
||||
cryptopro-csp:
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: ops/cryptopro/linux-csp-service/Dockerfile
|
||||
args:
|
||||
CRYPTOPRO_ACCEPT_EULA: "${CRYPTOPRO_ACCEPT_EULA:-0}"
|
||||
environment:
|
||||
ASPNETCORE_URLS: "http://0.0.0.0:8080"
|
||||
CRYPTOPRO_ACCEPT_EULA: "${CRYPTOPRO_ACCEPT_EULA:-0}"
|
||||
volumes:
|
||||
- ../../opt/cryptopro/downloads:/opt/cryptopro/downloads:ro
|
||||
ports:
|
||||
- "${CRYPTOPRO_PORT:-18080}:8080"
|
||||
labels: *release-labels
|
||||
networks: [stellaops]
|
||||
380
devops/compose/docker-compose.prod.yaml
Normal file
380
devops/compose/docker-compose.prod.yaml
Normal file
@@ -0,0 +1,380 @@
|
||||
x-release-labels: &release-labels
|
||||
com.stellaops.release.version: "2025.09.2"
|
||||
com.stellaops.release.channel: "stable"
|
||||
com.stellaops.profile: "prod"
|
||||
|
||||
networks:
|
||||
stellaops:
|
||||
driver: bridge
|
||||
frontdoor:
|
||||
external: true
|
||||
name: ${FRONTDOOR_NETWORK:-stellaops_frontdoor}
|
||||
|
||||
volumes:
|
||||
valkey-data:
|
||||
rustfs-data:
|
||||
concelier-jobs:
|
||||
nats-data:
|
||||
scanner-surface-cache:
|
||||
postgres-data:
|
||||
advisory-ai-queue:
|
||||
advisory-ai-plans:
|
||||
advisory-ai-outputs:
|
||||
|
||||
services:
|
||||
valkey:
|
||||
image: docker.io/valkey/valkey:8.0
|
||||
restart: unless-stopped
|
||||
command: ["valkey-server", "--appendonly", "yes"]
|
||||
volumes:
|
||||
- valkey-data:/data
|
||||
ports:
|
||||
- "${VALKEY_PORT:-6379}:6379"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
rustfs:
|
||||
image: registry.stella-ops.org/stellaops/rustfs:2025.10.0-edge
|
||||
command: ["serve", "--listen", "0.0.0.0:8080", "--root", "/data"]
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
RUSTFS__LOG__LEVEL: info
|
||||
RUSTFS__STORAGE__PATH: /data
|
||||
volumes:
|
||||
- rustfs-data:/data
|
||||
ports:
|
||||
- "${RUSTFS_HTTP_PORT:-8080}:8080"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
nats:
|
||||
image: docker.io/library/nats@sha256:c82559e4476289481a8a5196e675ebfe67eea81d95e5161e3e78eccfe766608e
|
||||
command:
|
||||
- "-js"
|
||||
- "-sd"
|
||||
- /data
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${NATS_CLIENT_PORT:-4222}:4222"
|
||||
volumes:
|
||||
- nats-data:/data
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
authority:
|
||||
image: registry.stella-ops.org/stellaops/authority@sha256:b0348bad1d0b401cc3c71cb40ba034c8043b6c8874546f90d4783c9dbfcc0bf5
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- valkey
|
||||
environment:
|
||||
STELLAOPS_AUTHORITY__ISSUER: "${AUTHORITY_ISSUER}"
|
||||
STELLAOPS_AUTHORITY__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_AUTHORITY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
STELLAOPS_AUTHORITY__CACHE__REDIS__CONNECTIONSTRING: "valkey:6379"
|
||||
STELLAOPS_AUTHORITY__PLUGINDIRECTORIES__0: "/app/plugins"
|
||||
STELLAOPS_AUTHORITY__PLUGINS__CONFIGURATIONDIRECTORY: "/app/etc/authority.plugins"
|
||||
volumes:
|
||||
- ../../etc/authority.yaml:/etc/authority.yaml:ro
|
||||
- ../../etc/authority.plugins:/app/etc/authority.plugins:ro
|
||||
ports:
|
||||
- "${AUTHORITY_PORT:-8440}:8440"
|
||||
networks:
|
||||
- stellaops
|
||||
- frontdoor
|
||||
labels: *release-labels
|
||||
|
||||
signer:
|
||||
image: registry.stella-ops.org/stellaops/signer@sha256:8ad574e61f3a9e9bda8a58eb2700ae46813284e35a150b1137bc7c2b92ac0f2e
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- authority
|
||||
environment:
|
||||
SIGNER__AUTHORITY__BASEURL: "https://authority:8440"
|
||||
SIGNER__POE__INTROSPECTURL: "${SIGNER_POE_INTROSPECT_URL}"
|
||||
SIGNER__STORAGE__DRIVER: "postgres"
|
||||
SIGNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
ports:
|
||||
- "${SIGNER_PORT:-8441}:8441"
|
||||
networks:
|
||||
- stellaops
|
||||
- frontdoor
|
||||
labels: *release-labels
|
||||
|
||||
attestor:
|
||||
image: registry.stella-ops.org/stellaops/attestor@sha256:0534985f978b0b5d220d73c96fddd962cd9135f616811cbe3bff4666c5af568f
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- signer
|
||||
- postgres
|
||||
environment:
|
||||
ATTESTOR__SIGNER__BASEURL: "https://signer:8441"
|
||||
ATTESTOR__STORAGE__DRIVER: "postgres"
|
||||
ATTESTOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
ports:
|
||||
- "${ATTESTOR_PORT:-8442}:8442"
|
||||
networks:
|
||||
- stellaops
|
||||
- frontdoor
|
||||
labels: *release-labels
|
||||
|
||||
postgres:
|
||||
image: docker.io/library/postgres:16
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: "${POSTGRES_USER:-stellaops}"
|
||||
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-stellaops}"
|
||||
POSTGRES_DB: "${POSTGRES_DB:-stellaops_platform}"
|
||||
PGDATA: /var/lib/postgresql/data/pgdata
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "${POSTGRES_PORT:-5432}:5432"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
issuer-directory:
|
||||
image: registry.stella-ops.org/stellaops/issuer-directory-web:2025.10.0-edge
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- authority
|
||||
environment:
|
||||
ISSUERDIRECTORY__CONFIG: "/etc/issuer-directory.yaml"
|
||||
ISSUERDIRECTORY__AUTHORITY__ISSUER: "${AUTHORITY_ISSUER}"
|
||||
ISSUERDIRECTORY__AUTHORITY__BASEURL: "https://authority:8440"
|
||||
ISSUERDIRECTORY__STORAGE__DRIVER: "postgres"
|
||||
ISSUERDIRECTORY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
ISSUERDIRECTORY__SEEDCSAFPUBLISHERS: "${ISSUER_DIRECTORY_SEED_CSAF:-true}"
|
||||
volumes:
|
||||
- ../../etc/issuer-directory.yaml:/etc/issuer-directory.yaml:ro
|
||||
ports:
|
||||
- "${ISSUER_DIRECTORY_PORT:-8447}:8080"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
concelier:
|
||||
image: registry.stella-ops.org/stellaops/concelier@sha256:c58cdcaee1d266d68d498e41110a589dd204b487d37381096bd61ab345a867c5
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- valkey
|
||||
environment:
|
||||
CONCELIER__STORAGE__DRIVER: "postgres"
|
||||
CONCELIER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
CONCELIER__STORAGE__S3__ENDPOINT: "http://rustfs:8080"
|
||||
CONCELIER__AUTHORITY__BASEURL: "https://authority:8440"
|
||||
CONCELIER__AUTHORITY__RESILIENCE__ALLOWOFFLINECACHEFALLBACK: "true"
|
||||
CONCELIER__AUTHORITY__RESILIENCE__OFFLINECACHETOLERANCE: "${AUTHORITY_OFFLINE_CACHE_TOLERANCE:-00:30:00}"
|
||||
volumes:
|
||||
- concelier-jobs:/var/lib/concelier/jobs
|
||||
ports:
|
||||
- "${CONCELIER_PORT:-8445}:8445"
|
||||
networks:
|
||||
- stellaops
|
||||
- frontdoor
|
||||
labels: *release-labels
|
||||
|
||||
scanner-web:
|
||||
image: registry.stella-ops.org/stellaops/scanner-web@sha256:14b23448c3f9586a9156370b3e8c1991b61907efa666ca37dd3aaed1e79fe3b7
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- valkey
|
||||
- concelier
|
||||
- rustfs
|
||||
environment:
|
||||
SCANNER__STORAGE__DRIVER: "postgres"
|
||||
SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
SCANNER__CACHE__REDIS__CONNECTIONSTRING: "valkey:6379"
|
||||
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
|
||||
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://rustfs:8080/api/v1"
|
||||
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
|
||||
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
|
||||
SCANNER__QUEUE__BROKER: "${SCANNER_QUEUE_BROKER:-valkey://valkey:6379}"
|
||||
SCANNER__EVENTS__ENABLED: "${SCANNER_EVENTS_ENABLED:-false}"
|
||||
SCANNER__EVENTS__DRIVER: "${SCANNER_EVENTS_DRIVER:-valkey}"
|
||||
SCANNER__EVENTS__DSN: "${SCANNER_EVENTS_DSN:-}"
|
||||
SCANNER__EVENTS__STREAM: "${SCANNER_EVENTS_STREAM:-stella.events}"
|
||||
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "${SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS:-5}"
|
||||
SCANNER__EVENTS__MAXSTREAMLENGTH: "${SCANNER_EVENTS_MAX_STREAM_LENGTH:-10000}"
|
||||
SCANNER__OFFLINEKIT__ENABLED: "${SCANNER_OFFLINEKIT_ENABLED:-false}"
|
||||
SCANNER__OFFLINEKIT__REQUIREDSSE: "${SCANNER_OFFLINEKIT_REQUIREDSSE:-true}"
|
||||
SCANNER__OFFLINEKIT__REKOROFFLINEMODE: "${SCANNER_OFFLINEKIT_REKOROFFLINEMODE:-true}"
|
||||
SCANNER__OFFLINEKIT__TRUSTROOTDIRECTORY: "${SCANNER_OFFLINEKIT_TRUSTROOTDIRECTORY:-/etc/stellaops/trust-roots}"
|
||||
SCANNER__OFFLINEKIT__REKORSNAPSHOTDIRECTORY: "${SCANNER_OFFLINEKIT_REKORSNAPSHOTDIRECTORY:-/var/lib/stellaops/rekor-snapshot}"
|
||||
SCANNER_SURFACE_FS_ENDPOINT: "${SCANNER_SURFACE_FS_ENDPOINT:-http://rustfs:8080}"
|
||||
SCANNER_SURFACE_FS_BUCKET: "${SCANNER_SURFACE_FS_BUCKET:-surface-cache}"
|
||||
SCANNER_SURFACE_CACHE_ROOT: "${SCANNER_SURFACE_CACHE_ROOT:-/var/lib/stellaops/surface}"
|
||||
SCANNER_SURFACE_CACHE_QUOTA_MB: "${SCANNER_SURFACE_CACHE_QUOTA_MB:-4096}"
|
||||
SCANNER_SURFACE_PREFETCH_ENABLED: "${SCANNER_SURFACE_PREFETCH_ENABLED:-false}"
|
||||
SCANNER_SURFACE_TENANT: "${SCANNER_SURFACE_TENANT:-default}"
|
||||
SCANNER_SURFACE_FEATURES: "${SCANNER_SURFACE_FEATURES:-}"
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER: "${SCANNER_SURFACE_SECRETS_PROVIDER:-file}"
|
||||
SCANNER_SURFACE_SECRETS_NAMESPACE: "${SCANNER_SURFACE_SECRETS_NAMESPACE:-}"
|
||||
SCANNER_SURFACE_SECRETS_ROOT: "${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}"
|
||||
SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER: "${SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER:-}"
|
||||
SCANNER_SURFACE_SECRETS_ALLOW_INLINE: "${SCANNER_SURFACE_SECRETS_ALLOW_INLINE:-false}"
|
||||
volumes:
|
||||
- scanner-surface-cache:/var/lib/stellaops/surface
|
||||
- ${SURFACE_SECRETS_HOST_PATH:-./offline/surface-secrets}:${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}:ro
|
||||
- ${SCANNER_OFFLINEKIT_TRUSTROOTS_HOST_PATH:-./offline/trust-roots}:${SCANNER_OFFLINEKIT_TRUSTROOTDIRECTORY:-/etc/stellaops/trust-roots}:ro
|
||||
- ${SCANNER_OFFLINEKIT_REKOR_SNAPSHOT_HOST_PATH:-./offline/rekor-snapshot}:${SCANNER_OFFLINEKIT_REKORSNAPSHOTDIRECTORY:-/var/lib/stellaops/rekor-snapshot}:ro
|
||||
ports:
|
||||
- "${SCANNER_WEB_PORT:-8444}:8444"
|
||||
networks:
|
||||
- stellaops
|
||||
- frontdoor
|
||||
labels: *release-labels
|
||||
|
||||
scanner-worker:
|
||||
image: registry.stella-ops.org/stellaops/scanner-worker@sha256:32e25e76386eb9ea8bee0a1ad546775db9a2df989fab61ac877e351881960dab
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- valkey
|
||||
- scanner-web
|
||||
- rustfs
|
||||
environment:
|
||||
SCANNER__STORAGE__DRIVER: "postgres"
|
||||
SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
SCANNER__CACHE__REDIS__CONNECTIONSTRING: "valkey:6379"
|
||||
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
|
||||
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://rustfs:8080/api/v1"
|
||||
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
|
||||
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
|
||||
SCANNER__QUEUE__BROKER: "${SCANNER_QUEUE_BROKER:-valkey://valkey:6379}"
|
||||
SCANNER_SURFACE_FS_ENDPOINT: "${SCANNER_SURFACE_FS_ENDPOINT:-http://rustfs:8080}"
|
||||
SCANNER_SURFACE_FS_BUCKET: "${SCANNER_SURFACE_FS_BUCKET:-surface-cache}"
|
||||
SCANNER_SURFACE_CACHE_ROOT: "${SCANNER_SURFACE_CACHE_ROOT:-/var/lib/stellaops/surface}"
|
||||
SCANNER_SURFACE_CACHE_QUOTA_MB: "${SCANNER_SURFACE_CACHE_QUOTA_MB:-4096}"
|
||||
SCANNER_SURFACE_PREFETCH_ENABLED: "${SCANNER_SURFACE_PREFETCH_ENABLED:-false}"
|
||||
SCANNER_SURFACE_TENANT: "${SCANNER_SURFACE_TENANT:-default}"
|
||||
SCANNER_SURFACE_FEATURES: "${SCANNER_SURFACE_FEATURES:-}"
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER: "${SCANNER_SURFACE_SECRETS_PROVIDER:-file}"
|
||||
SCANNER_SURFACE_SECRETS_NAMESPACE: "${SCANNER_SURFACE_SECRETS_NAMESPACE:-}"
|
||||
SCANNER_SURFACE_SECRETS_ROOT: "${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}"
|
||||
SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER: "${SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER:-}"
|
||||
SCANNER_SURFACE_SECRETS_ALLOW_INLINE: "${SCANNER_SURFACE_SECRETS_ALLOW_INLINE:-false}"
|
||||
volumes:
|
||||
- scanner-surface-cache:/var/lib/stellaops/surface
|
||||
- ${SURFACE_SECRETS_HOST_PATH:-./offline/surface-secrets}:${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}:ro
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
scheduler-worker:
|
||||
image: registry.stella-ops.org/stellaops/scheduler-worker:2025.10.0-edge
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- valkey
|
||||
- scanner-web
|
||||
command:
|
||||
- "dotnet"
|
||||
- "StellaOps.Scheduler.Worker.Host.dll"
|
||||
environment:
|
||||
SCHEDULER__STORAGE__DRIVER: "postgres"
|
||||
SCHEDULER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
SCHEDULER__QUEUE__KIND: "${SCHEDULER_QUEUE_KIND:-Valkey}"
|
||||
SCHEDULER__QUEUE__VALKEY__URL: "${SCHEDULER_QUEUE_VALKEY_URL:-valkey:6379}"
|
||||
SCHEDULER__WORKER__RUNNER__SCANNER__BASEADDRESS: "${SCHEDULER_SCANNER_BASEADDRESS:-http://scanner-web:8444}"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
notify-web:
|
||||
image: ${NOTIFY_WEB_IMAGE:-registry.stella-ops.org/stellaops/notify-web:2025.09.2}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- authority
|
||||
environment:
|
||||
DOTNET_ENVIRONMENT: Production
|
||||
volumes:
|
||||
- ../../etc/notify.prod.yaml:/app/etc/notify.yaml:ro
|
||||
ports:
|
||||
- "${NOTIFY_WEB_PORT:-8446}:8446"
|
||||
networks:
|
||||
- stellaops
|
||||
- frontdoor
|
||||
labels: *release-labels
|
||||
|
||||
excititor:
|
||||
image: registry.stella-ops.org/stellaops/excititor@sha256:59022e2016aebcef5c856d163ae705755d3f81949d41195256e935ef40a627fa
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- concelier
|
||||
environment:
|
||||
EXCITITOR__CONCELIER__BASEURL: "https://concelier:8445"
|
||||
EXCITITOR__STORAGE__DRIVER: "postgres"
|
||||
EXCITITOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
advisory-ai-web:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-web:2025.09.2
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- scanner-web
|
||||
environment:
|
||||
ADVISORYAI__AdvisoryAI__SbomBaseAddress: "${ADVISORY_AI_SBOM_BASEADDRESS:-http://scanner-web:8444}"
|
||||
ADVISORYAI__AdvisoryAI__Queue__DirectoryPath: "/var/lib/advisory-ai/queue"
|
||||
ADVISORYAI__AdvisoryAI__Storage__PlanCacheDirectory: "/var/lib/advisory-ai/plans"
|
||||
ADVISORYAI__AdvisoryAI__Storage__OutputDirectory: "/var/lib/advisory-ai/outputs"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Mode: "${ADVISORY_AI_INFERENCE_MODE:-Local}"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__BaseAddress: "${ADVISORY_AI_REMOTE_BASEADDRESS:-}"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__ApiKey: "${ADVISORY_AI_REMOTE_APIKEY:-}"
|
||||
ports:
|
||||
- "${ADVISORY_AI_WEB_PORT:-8448}:8448"
|
||||
volumes:
|
||||
- advisory-ai-queue:/var/lib/advisory-ai/queue
|
||||
- advisory-ai-plans:/var/lib/advisory-ai/plans
|
||||
- advisory-ai-outputs:/var/lib/advisory-ai/outputs
|
||||
networks:
|
||||
- stellaops
|
||||
- frontdoor
|
||||
labels: *release-labels
|
||||
|
||||
advisory-ai-worker:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-worker:2025.09.2
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- advisory-ai-web
|
||||
environment:
|
||||
ADVISORYAI__AdvisoryAI__SbomBaseAddress: "${ADVISORY_AI_SBOM_BASEADDRESS:-http://scanner-web:8444}"
|
||||
ADVISORYAI__AdvisoryAI__Queue__DirectoryPath: "/var/lib/advisory-ai/queue"
|
||||
ADVISORYAI__AdvisoryAI__Storage__PlanCacheDirectory: "/var/lib/advisory-ai/plans"
|
||||
ADVISORYAI__AdvisoryAI__Storage__OutputDirectory: "/var/lib/advisory-ai/outputs"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Mode: "${ADVISORY_AI_INFERENCE_MODE:-Local}"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__BaseAddress: "${ADVISORY_AI_REMOTE_BASEADDRESS:-}"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__ApiKey: "${ADVISORY_AI_REMOTE_APIKEY:-}"
|
||||
volumes:
|
||||
- advisory-ai-queue:/var/lib/advisory-ai/queue
|
||||
- advisory-ai-plans:/var/lib/advisory-ai/plans
|
||||
- advisory-ai-outputs:/var/lib/advisory-ai/outputs
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
web-ui:
|
||||
image: registry.stella-ops.org/stellaops/web-ui@sha256:10d924808c48e4353e3a241da62eb7aefe727a1d6dc830eb23a8e181013b3a23
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- scanner-web
|
||||
environment:
|
||||
STELLAOPS_UI__BACKEND__BASEURL: "https://scanner-web:8444"
|
||||
ports:
|
||||
- "${UI_PORT:-8443}:8443"
|
||||
networks:
|
||||
- stellaops
|
||||
- frontdoor
|
||||
labels: *release-labels
|
||||
301
devops/compose/docker-compose.russia.yml
Normal file
301
devops/compose/docker-compose.russia.yml
Normal file
@@ -0,0 +1,301 @@
|
||||
# StellaOps Docker Compose - International Profile
|
||||
# Cryptography: GOST R 34.10-2012, GOST R 34.11-2012 (Streebog)
|
||||
# Provider: openssl.gost, pkcs11.gost, cryptopro.gost
|
||||
# Jurisdiction: world
|
||||
|
||||
x-release-labels: &release-labels
|
||||
com.stellaops.release.version: "2025.10.0-edge"
|
||||
com.stellaops.release.channel: "edge"
|
||||
com.stellaops.profile: "russia"
|
||||
com.stellaops.crypto.profile: "russia"
|
||||
com.stellaops.crypto.provider: "openssl.gost, pkcs11.gost, cryptopro.gost"
|
||||
|
||||
x-crypto-env: &crypto-env
|
||||
# Crypto configuration
|
||||
STELLAOPS_CRYPTO_PROFILE: "russia"
|
||||
STELLAOPS_CRYPTO_CONFIG_PATH: "/app/etc/appsettings.crypto.yaml"
|
||||
STELLAOPS_CRYPTO_MANIFEST_PATH: "/app/etc/crypto-plugins-manifest.json"
|
||||
|
||||
networks:
|
||||
stellaops:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
rustfs-data:
|
||||
concelier-jobs:
|
||||
nats-data:
|
||||
valkey-data:
|
||||
advisory-ai-queue:
|
||||
advisory-ai-plans:
|
||||
advisory-ai-outputs:
|
||||
postgres-data:
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: docker.io/library/postgres:16
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: "${POSTGRES_USER:-stellaops}"
|
||||
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-stellaops}"
|
||||
POSTGRES_DB: "${POSTGRES_DB:-stellaops_platform}"
|
||||
PGDATA: /var/lib/postgresql/data/pgdata
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
- ../postgres-partitioning:/docker-entrypoint-initdb.d:ro
|
||||
ports:
|
||||
- "${POSTGRES_PORT:-5432}:5432"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
valkey:
|
||||
image: docker.io/valkey/valkey:8.0
|
||||
restart: unless-stopped
|
||||
command: ["valkey-server", "--appendonly", "yes"]
|
||||
volumes:
|
||||
- valkey-data:/data
|
||||
ports:
|
||||
- "${VALKEY_PORT:-6379}:6379"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
rustfs:
|
||||
image: registry.stella-ops.org/stellaops/rustfs:2025.10.0-edge
|
||||
command: ["serve", "--listen", "0.0.0.0:8080", "--root", "/data"]
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
RUSTFS__LOG__LEVEL: info
|
||||
RUSTFS__STORAGE__PATH: /data
|
||||
volumes:
|
||||
- rustfs-data:/data
|
||||
ports:
|
||||
- "${RUSTFS_HTTP_PORT:-8080}:8080"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
nats:
|
||||
image: docker.io/library/nats@sha256:c82559e4476289481a8a5196e675ebfe67eea81d95e5161e3e78eccfe766608e
|
||||
command:
|
||||
- "-js"
|
||||
- "-sd"
|
||||
- /data
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${NATS_CLIENT_PORT:-4222}:4222"
|
||||
volumes:
|
||||
- nats-data:/data
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
authority:
|
||||
image: registry.stella-ops.org/stellaops/authority:russia
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_AUTHORITY__ISSUER: "${AUTHORITY_ISSUER}"
|
||||
STELLAOPS_AUTHORITY__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_AUTHORITY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
STELLAOPS_AUTHORITY__PLUGINDIRECTORIES__0: "/app/plugins"
|
||||
STELLAOPS_AUTHORITY__PLUGINS__CONFIGURATIONDIRECTORY: "/app/etc/authority.plugins"
|
||||
volumes:
|
||||
- ../../etc/authority.yaml:/etc/authority.yaml:ro
|
||||
- ../../etc/authority.plugins:/app/etc/authority.plugins:ro
|
||||
- ../../etc/appsettings.crypto.russia.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${AUTHORITY_PORT:-8440}:8440"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
signer:
|
||||
image: registry.stella-ops.org/stellaops/signer:russia
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_SIGNER__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_SIGNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.russia.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${SIGNER_PORT:-8441}:8441"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
attestor:
|
||||
image: registry.stella-ops.org/stellaops/attestor:russia
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- signer
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_ATTESTOR__SIGNER__BASEURL: "http://signer:8441"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.russia.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${ATTESTOR_PORT:-8442}:8442"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
concelier:
|
||||
image: registry.stella-ops.org/stellaops/concelier:russia
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- rustfs
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_CONCELIER__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_CONCELIER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
STELLAOPS_CONCELIER__STORAGE__RUSTFS__BASEURL: "http://rustfs:8080"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.russia.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
- concelier-jobs:/app/jobs
|
||||
ports:
|
||||
- "${CONCELIER_PORT:-8443}:8443"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
scanner:
|
||||
image: registry.stella-ops.org/stellaops/scanner:russia
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_SCANNER__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.russia.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${SCANNER_PORT:-8444}:8444"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
excititor:
|
||||
image: registry.stella-ops.org/stellaops/excititor:russia
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_EXCITITOR__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_EXCITITOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.russia.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${EXCITITOR_PORT:-8445}:8445"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
policy:
|
||||
image: registry.stella-ops.org/stellaops/policy:russia
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_POLICY__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_POLICY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.russia.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${POLICY_PORT:-8446}:8446"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
scheduler:
|
||||
image: registry.stella-ops.org/stellaops/scheduler:russia
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- nats
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_SCHEDULER__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_SCHEDULER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
STELLAOPS_SCHEDULER__MESSAGING__NATS__URL: "nats://nats:4222"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.russia.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${SCHEDULER_PORT:-8447}:8447"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
notify:
|
||||
image: registry.stella-ops.org/stellaops/notify:russia
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_NOTIFY__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_NOTIFY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.russia.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${NOTIFY_PORT:-8448}:8448"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
zastava:
|
||||
image: registry.stella-ops.org/stellaops/zastava:russia
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_ZASTAVA__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_ZASTAVA__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.russia.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${ZASTAVA_PORT:-8449}:8449"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
gateway:
|
||||
image: registry.stella-ops.org/stellaops/gateway:russia
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- authority
|
||||
- concelier
|
||||
- scanner
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
STELLAOPS_GATEWAY__AUTHORITY__BASEURL: "http://authority:8440"
|
||||
STELLAOPS_GATEWAY__CONCELIER__BASEURL: "http://concelier:8443"
|
||||
STELLAOPS_GATEWAY__SCANNER__BASEURL: "http://scanner:8444"
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.russia.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
ports:
|
||||
- "${GATEWAY_PORT:-8080}:8080"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
369
devops/compose/docker-compose.stage.yaml
Normal file
369
devops/compose/docker-compose.stage.yaml
Normal file
@@ -0,0 +1,369 @@
|
||||
x-release-labels: &release-labels
|
||||
com.stellaops.release.version: "2025.09.2"
|
||||
com.stellaops.release.channel: "stable"
|
||||
com.stellaops.profile: "stage"
|
||||
|
||||
networks:
|
||||
stellaops:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
valkey-data:
|
||||
rustfs-data:
|
||||
concelier-jobs:
|
||||
nats-data:
|
||||
scanner-surface-cache:
|
||||
postgres-data:
|
||||
advisory-ai-queue:
|
||||
advisory-ai-plans:
|
||||
advisory-ai-outputs:
|
||||
|
||||
services:
|
||||
valkey:
|
||||
image: docker.io/valkey/valkey:8.0
|
||||
restart: unless-stopped
|
||||
command: ["valkey-server", "--appendonly", "yes"]
|
||||
volumes:
|
||||
- valkey-data:/data
|
||||
ports:
|
||||
- "${VALKEY_PORT:-6379}:6379"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
postgres:
|
||||
image: docker.io/library/postgres:16
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: "${POSTGRES_USER:-stellaops}"
|
||||
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-stellaops}"
|
||||
POSTGRES_DB: "${POSTGRES_DB:-stellaops_platform}"
|
||||
PGDATA: /var/lib/postgresql/data/pgdata
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "${POSTGRES_PORT:-5432}:5432"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
rustfs:
|
||||
image: registry.stella-ops.org/stellaops/rustfs:2025.10.0-edge
|
||||
command: ["serve", "--listen", "0.0.0.0:8080", "--root", "/data"]
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
RUSTFS__LOG__LEVEL: info
|
||||
RUSTFS__STORAGE__PATH: /data
|
||||
volumes:
|
||||
- rustfs-data:/data
|
||||
ports:
|
||||
- "${RUSTFS_HTTP_PORT:-8080}:8080"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
nats:
|
||||
image: docker.io/library/nats@sha256:c82559e4476289481a8a5196e675ebfe67eea81d95e5161e3e78eccfe766608e
|
||||
command:
|
||||
- "-js"
|
||||
- "-sd"
|
||||
- /data
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${NATS_CLIENT_PORT:-4222}:4222"
|
||||
volumes:
|
||||
- nats-data:/data
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
authority:
|
||||
image: registry.stella-ops.org/stellaops/authority@sha256:b0348bad1d0b401cc3c71cb40ba034c8043b6c8874546f90d4783c9dbfcc0bf5
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- valkey
|
||||
environment:
|
||||
STELLAOPS_AUTHORITY__ISSUER: "${AUTHORITY_ISSUER}"
|
||||
STELLAOPS_AUTHORITY__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_AUTHORITY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
STELLAOPS_AUTHORITY__CACHE__REDIS__CONNECTIONSTRING: "valkey:6379"
|
||||
STELLAOPS_AUTHORITY__PLUGINDIRECTORIES__0: "/app/plugins"
|
||||
STELLAOPS_AUTHORITY__PLUGINS__CONFIGURATIONDIRECTORY: "/app/etc/authority.plugins"
|
||||
volumes:
|
||||
- ../../etc/authority.yaml:/etc/authority.yaml:ro
|
||||
- ../../etc/authority.plugins:/app/etc/authority.plugins:ro
|
||||
ports:
|
||||
- "${AUTHORITY_PORT:-8440}:8440"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
signer:
|
||||
image: registry.stella-ops.org/stellaops/signer@sha256:8ad574e61f3a9e9bda8a58eb2700ae46813284e35a150b1137bc7c2b92ac0f2e
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- authority
|
||||
environment:
|
||||
SIGNER__AUTHORITY__BASEURL: "https://authority:8440"
|
||||
SIGNER__POE__INTROSPECTURL: "${SIGNER_POE_INTROSPECT_URL}"
|
||||
SIGNER__STORAGE__DRIVER: "postgres"
|
||||
SIGNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
ports:
|
||||
- "${SIGNER_PORT:-8441}:8441"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
attestor:
|
||||
image: registry.stella-ops.org/stellaops/attestor@sha256:0534985f978b0b5d220d73c96fddd962cd9135f616811cbe3bff4666c5af568f
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- signer
|
||||
- postgres
|
||||
environment:
|
||||
ATTESTOR__SIGNER__BASEURL: "https://signer:8441"
|
||||
ATTESTOR__STORAGE__DRIVER: "postgres"
|
||||
ATTESTOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
ports:
|
||||
- "${ATTESTOR_PORT:-8442}:8442"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
issuer-directory:
|
||||
image: registry.stella-ops.org/stellaops/issuer-directory-web:2025.10.0-edge
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- authority
|
||||
environment:
|
||||
ISSUERDIRECTORY__CONFIG: "/etc/issuer-directory.yaml"
|
||||
ISSUERDIRECTORY__AUTHORITY__ISSUER: "${AUTHORITY_ISSUER}"
|
||||
ISSUERDIRECTORY__AUTHORITY__BASEURL: "https://authority:8440"
|
||||
ISSUERDIRECTORY__STORAGE__DRIVER: "postgres"
|
||||
ISSUERDIRECTORY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
ISSUERDIRECTORY__SEEDCSAFPUBLISHERS: "${ISSUER_DIRECTORY_SEED_CSAF:-true}"
|
||||
volumes:
|
||||
- ../../etc/issuer-directory.yaml:/etc/issuer-directory.yaml:ro
|
||||
ports:
|
||||
- "${ISSUER_DIRECTORY_PORT:-8447}:8080"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
concelier:
|
||||
image: registry.stella-ops.org/stellaops/concelier@sha256:c58cdcaee1d266d68d498e41110a589dd204b487d37381096bd61ab345a867c5
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- valkey
|
||||
environment:
|
||||
CONCELIER__STORAGE__DRIVER: "postgres"
|
||||
CONCELIER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
CONCELIER__STORAGE__S3__ENDPOINT: "http://rustfs:8080"
|
||||
CONCELIER__AUTHORITY__BASEURL: "https://authority:8440"
|
||||
CONCELIER__AUTHORITY__RESILIENCE__ALLOWOFFLINECACHEFALLBACK: "true"
|
||||
CONCELIER__AUTHORITY__RESILIENCE__OFFLINECACHETOLERANCE: "${AUTHORITY_OFFLINE_CACHE_TOLERANCE:-00:30:00}"
|
||||
volumes:
|
||||
- concelier-jobs:/var/lib/concelier/jobs
|
||||
ports:
|
||||
- "${CONCELIER_PORT:-8445}:8445"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
scanner-web:
|
||||
image: registry.stella-ops.org/stellaops/scanner-web@sha256:14b23448c3f9586a9156370b3e8c1991b61907efa666ca37dd3aaed1e79fe3b7
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- valkey
|
||||
- concelier
|
||||
- rustfs
|
||||
environment:
|
||||
SCANNER__STORAGE__DRIVER: "postgres"
|
||||
SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
SCANNER__CACHE__REDIS__CONNECTIONSTRING: "valkey:6379"
|
||||
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
|
||||
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://rustfs:8080/api/v1"
|
||||
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
|
||||
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
|
||||
SCANNER__QUEUE__BROKER: "${SCANNER_QUEUE_BROKER:-valkey://valkey:6379}"
|
||||
SCANNER__EVENTS__ENABLED: "${SCANNER_EVENTS_ENABLED:-false}"
|
||||
SCANNER__EVENTS__DRIVER: "${SCANNER_EVENTS_DRIVER:-valkey}"
|
||||
SCANNER__EVENTS__DSN: "${SCANNER_EVENTS_DSN:-}"
|
||||
SCANNER__EVENTS__STREAM: "${SCANNER_EVENTS_STREAM:-stella.events}"
|
||||
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "${SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS:-5}"
|
||||
SCANNER__EVENTS__MAXSTREAMLENGTH: "${SCANNER_EVENTS_MAX_STREAM_LENGTH:-10000}"
|
||||
SCANNER__OFFLINEKIT__ENABLED: "${SCANNER_OFFLINEKIT_ENABLED:-false}"
|
||||
SCANNER__OFFLINEKIT__REQUIREDSSE: "${SCANNER_OFFLINEKIT_REQUIREDSSE:-true}"
|
||||
SCANNER__OFFLINEKIT__REKOROFFLINEMODE: "${SCANNER_OFFLINEKIT_REKOROFFLINEMODE:-true}"
|
||||
SCANNER__OFFLINEKIT__TRUSTROOTDIRECTORY: "${SCANNER_OFFLINEKIT_TRUSTROOTDIRECTORY:-/etc/stellaops/trust-roots}"
|
||||
SCANNER__OFFLINEKIT__REKORSNAPSHOTDIRECTORY: "${SCANNER_OFFLINEKIT_REKORSNAPSHOTDIRECTORY:-/var/lib/stellaops/rekor-snapshot}"
|
||||
SCANNER_SURFACE_FS_ENDPOINT: "${SCANNER_SURFACE_FS_ENDPOINT:-http://rustfs:8080}"
|
||||
SCANNER_SURFACE_FS_BUCKET: "${SCANNER_SURFACE_FS_BUCKET:-surface-cache}"
|
||||
SCANNER_SURFACE_CACHE_ROOT: "${SCANNER_SURFACE_CACHE_ROOT:-/var/lib/stellaops/surface}"
|
||||
SCANNER_SURFACE_CACHE_QUOTA_MB: "${SCANNER_SURFACE_CACHE_QUOTA_MB:-4096}"
|
||||
SCANNER_SURFACE_PREFETCH_ENABLED: "${SCANNER_SURFACE_PREFETCH_ENABLED:-false}"
|
||||
SCANNER_SURFACE_TENANT: "${SCANNER_SURFACE_TENANT:-default}"
|
||||
SCANNER_SURFACE_FEATURES: "${SCANNER_SURFACE_FEATURES:-}"
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER: "${SCANNER_SURFACE_SECRETS_PROVIDER:-file}"
|
||||
SCANNER_SURFACE_SECRETS_NAMESPACE: "${SCANNER_SURFACE_SECRETS_NAMESPACE:-}"
|
||||
SCANNER_SURFACE_SECRETS_ROOT: "${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}"
|
||||
SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER: "${SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER:-}"
|
||||
SCANNER_SURFACE_SECRETS_ALLOW_INLINE: "${SCANNER_SURFACE_SECRETS_ALLOW_INLINE:-false}"
|
||||
volumes:
|
||||
- scanner-surface-cache:/var/lib/stellaops/surface
|
||||
- ${SURFACE_SECRETS_HOST_PATH:-./offline/surface-secrets}:${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}:ro
|
||||
- ${SCANNER_OFFLINEKIT_TRUSTROOTS_HOST_PATH:-./offline/trust-roots}:${SCANNER_OFFLINEKIT_TRUSTROOTDIRECTORY:-/etc/stellaops/trust-roots}:ro
|
||||
- ${SCANNER_OFFLINEKIT_REKOR_SNAPSHOT_HOST_PATH:-./offline/rekor-snapshot}:${SCANNER_OFFLINEKIT_REKORSNAPSHOTDIRECTORY:-/var/lib/stellaops/rekor-snapshot}:ro
|
||||
ports:
|
||||
- "${SCANNER_WEB_PORT:-8444}:8444"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
scanner-worker:
|
||||
image: registry.stella-ops.org/stellaops/scanner-worker@sha256:32e25e76386eb9ea8bee0a1ad546775db9a2df989fab61ac877e351881960dab
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- valkey
|
||||
- scanner-web
|
||||
- rustfs
|
||||
environment:
|
||||
SCANNER__STORAGE__DRIVER: "postgres"
|
||||
SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
SCANNER__CACHE__REDIS__CONNECTIONSTRING: "valkey:6379"
|
||||
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
|
||||
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://rustfs:8080/api/v1"
|
||||
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
|
||||
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
|
||||
SCANNER__QUEUE__BROKER: "${SCANNER_QUEUE_BROKER:-valkey://valkey:6379}"
|
||||
SCANNER_SURFACE_FS_ENDPOINT: "${SCANNER_SURFACE_FS_ENDPOINT:-http://rustfs:8080}"
|
||||
SCANNER_SURFACE_FS_BUCKET: "${SCANNER_SURFACE_FS_BUCKET:-surface-cache}"
|
||||
SCANNER_SURFACE_CACHE_ROOT: "${SCANNER_SURFACE_CACHE_ROOT:-/var/lib/stellaops/surface}"
|
||||
SCANNER_SURFACE_CACHE_QUOTA_MB: "${SCANNER_SURFACE_CACHE_QUOTA_MB:-4096}"
|
||||
SCANNER_SURFACE_PREFETCH_ENABLED: "${SCANNER_SURFACE_PREFETCH_ENABLED:-false}"
|
||||
SCANNER_SURFACE_TENANT: "${SCANNER_SURFACE_TENANT:-default}"
|
||||
SCANNER_SURFACE_FEATURES: "${SCANNER_SURFACE_FEATURES:-}"
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER: "${SCANNER_SURFACE_SECRETS_PROVIDER:-file}"
|
||||
SCANNER_SURFACE_SECRETS_NAMESPACE: "${SCANNER_SURFACE_SECRETS_NAMESPACE:-}"
|
||||
SCANNER_SURFACE_SECRETS_ROOT: "${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}"
|
||||
SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER: "${SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER:-}"
|
||||
SCANNER_SURFACE_SECRETS_ALLOW_INLINE: "${SCANNER_SURFACE_SECRETS_ALLOW_INLINE:-false}"
|
||||
volumes:
|
||||
- scanner-surface-cache:/var/lib/stellaops/surface
|
||||
- ${SURFACE_SECRETS_HOST_PATH:-./offline/surface-secrets}:${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}:ro
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
scheduler-worker:
|
||||
image: registry.stella-ops.org/stellaops/scheduler-worker:2025.10.0-edge
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- valkey
|
||||
- scanner-web
|
||||
command:
|
||||
- "dotnet"
|
||||
- "StellaOps.Scheduler.Worker.Host.dll"
|
||||
environment:
|
||||
SCHEDULER__STORAGE__DRIVER: "postgres"
|
||||
SCHEDULER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
SCHEDULER__QUEUE__KIND: "${SCHEDULER_QUEUE_KIND:-Valkey}"
|
||||
SCHEDULER__QUEUE__VALKEY__URL: "${SCHEDULER_QUEUE_VALKEY_URL:-valkey:6379}"
|
||||
SCHEDULER__WORKER__RUNNER__SCANNER__BASEADDRESS: "${SCHEDULER_SCANNER_BASEADDRESS:-http://scanner-web:8444}"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
notify-web:
|
||||
image: ${NOTIFY_WEB_IMAGE:-registry.stella-ops.org/stellaops/notify-web:2025.09.2}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- authority
|
||||
environment:
|
||||
DOTNET_ENVIRONMENT: Production
|
||||
volumes:
|
||||
- ../../etc/notify.stage.yaml:/app/etc/notify.yaml:ro
|
||||
ports:
|
||||
- "${NOTIFY_WEB_PORT:-8446}:8446"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
excititor:
|
||||
image: registry.stella-ops.org/stellaops/excititor@sha256:59022e2016aebcef5c856d163ae705755d3f81949d41195256e935ef40a627fa
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- concelier
|
||||
environment:
|
||||
EXCITITOR__CONCELIER__BASEURL: "https://concelier:8445"
|
||||
EXCITITOR__STORAGE__DRIVER: "postgres"
|
||||
EXCITITOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
advisory-ai-web:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-web:2025.09.2
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- scanner-web
|
||||
environment:
|
||||
ADVISORYAI__AdvisoryAI__SbomBaseAddress: "${ADVISORY_AI_SBOM_BASEADDRESS:-http://scanner-web:8444}"
|
||||
ADVISORYAI__AdvisoryAI__Queue__DirectoryPath: "/var/lib/advisory-ai/queue"
|
||||
ADVISORYAI__AdvisoryAI__Storage__PlanCacheDirectory: "/var/lib/advisory-ai/plans"
|
||||
ADVISORYAI__AdvisoryAI__Storage__OutputDirectory: "/var/lib/advisory-ai/outputs"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Mode: "${ADVISORY_AI_INFERENCE_MODE:-Local}"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__BaseAddress: "${ADVISORY_AI_REMOTE_BASEADDRESS:-}"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__ApiKey: "${ADVISORY_AI_REMOTE_APIKEY:-}"
|
||||
ports:
|
||||
- "${ADVISORY_AI_WEB_PORT:-8448}:8448"
|
||||
volumes:
|
||||
- advisory-ai-queue:/var/lib/advisory-ai/queue
|
||||
- advisory-ai-plans:/var/lib/advisory-ai/plans
|
||||
- advisory-ai-outputs:/var/lib/advisory-ai/outputs
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
advisory-ai-worker:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-worker:2025.09.2
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- advisory-ai-web
|
||||
environment:
|
||||
ADVISORYAI__AdvisoryAI__SbomBaseAddress: "${ADVISORY_AI_SBOM_BASEADDRESS:-http://scanner-web:8444}"
|
||||
ADVISORYAI__AdvisoryAI__Queue__DirectoryPath: "/var/lib/advisory-ai/queue"
|
||||
ADVISORYAI__AdvisoryAI__Storage__PlanCacheDirectory: "/var/lib/advisory-ai/plans"
|
||||
ADVISORYAI__AdvisoryAI__Storage__OutputDirectory: "/var/lib/advisory-ai/outputs"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Mode: "${ADVISORY_AI_INFERENCE_MODE:-Local}"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__BaseAddress: "${ADVISORY_AI_REMOTE_BASEADDRESS:-}"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__ApiKey: "${ADVISORY_AI_REMOTE_APIKEY:-}"
|
||||
volumes:
|
||||
- advisory-ai-queue:/var/lib/advisory-ai/queue
|
||||
- advisory-ai-plans:/var/lib/advisory-ai/plans
|
||||
- advisory-ai-outputs:/var/lib/advisory-ai/outputs
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
web-ui:
|
||||
image: registry.stella-ops.org/stellaops/web-ui@sha256:10d924808c48e4353e3a241da62eb7aefe727a1d6dc830eb23a8e181013b3a23
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- scanner-web
|
||||
environment:
|
||||
STELLAOPS_UI__BACKEND__BASEURL: "https://scanner-web:8444"
|
||||
ports:
|
||||
- "${UI_PORT:-8443}:8443"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
57
devops/compose/docker-compose.telemetry-storage.yaml
Normal file
57
devops/compose/docker-compose.telemetry-storage.yaml
Normal file
@@ -0,0 +1,57 @@
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
prometheus:
|
||||
image: prom/prometheus:v2.53.0
|
||||
container_name: stellaops-prometheus
|
||||
command:
|
||||
- "--config.file=/etc/prometheus/prometheus.yaml"
|
||||
volumes:
|
||||
- ../telemetry/storage/prometheus.yaml:/etc/prometheus/prometheus.yaml:ro
|
||||
- prometheus-data:/prometheus
|
||||
- ../telemetry/certs:/etc/telemetry/tls:ro
|
||||
- ../telemetry/storage/auth:/etc/telemetry/auth:ro
|
||||
environment:
|
||||
PROMETHEUS_COLLECTOR_TARGET: stellaops-otel-collector:9464
|
||||
ports:
|
||||
- "9090:9090"
|
||||
depends_on:
|
||||
- tempo
|
||||
- loki
|
||||
|
||||
tempo:
|
||||
image: grafana/tempo:2.5.0
|
||||
container_name: stellaops-tempo
|
||||
command:
|
||||
- "-config.file=/etc/tempo/tempo.yaml"
|
||||
volumes:
|
||||
- ../telemetry/storage/tempo.yaml:/etc/tempo/tempo.yaml:ro
|
||||
- ../telemetry/storage/tenants/tempo-overrides.yaml:/etc/telemetry/tenants/tempo-overrides.yaml:ro
|
||||
- ../telemetry/certs:/etc/telemetry/tls:ro
|
||||
- tempo-data:/var/tempo
|
||||
ports:
|
||||
- "3200:3200"
|
||||
environment:
|
||||
TEMPO_ZONE: docker
|
||||
|
||||
loki:
|
||||
image: grafana/loki:3.1.0
|
||||
container_name: stellaops-loki
|
||||
command:
|
||||
- "-config.file=/etc/loki/loki.yaml"
|
||||
volumes:
|
||||
- ../telemetry/storage/loki.yaml:/etc/loki/loki.yaml:ro
|
||||
- ../telemetry/storage/tenants/loki-overrides.yaml:/etc/telemetry/tenants/loki-overrides.yaml:ro
|
||||
- ../telemetry/certs:/etc/telemetry/tls:ro
|
||||
- loki-data:/var/loki
|
||||
ports:
|
||||
- "3100:3100"
|
||||
|
||||
volumes:
|
||||
prometheus-data:
|
||||
tempo-data:
|
||||
loki-data:
|
||||
|
||||
networks:
|
||||
default:
|
||||
name: stellaops-telemetry
|
||||
42
devops/compose/docker-compose.telemetry.yaml
Normal file
42
devops/compose/docker-compose.telemetry.yaml
Normal file
@@ -0,0 +1,42 @@
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
otel-collector:
|
||||
image: otel/opentelemetry-collector:0.105.0
|
||||
container_name: stellaops-otel-collector
|
||||
command:
|
||||
- "--config=/etc/otel-collector/config.yaml"
|
||||
environment:
|
||||
STELLAOPS_OTEL_TLS_CERT: /etc/otel-collector/tls/collector.crt
|
||||
STELLAOPS_OTEL_TLS_KEY: /etc/otel-collector/tls/collector.key
|
||||
STELLAOPS_OTEL_TLS_CA: /etc/otel-collector/tls/ca.crt
|
||||
STELLAOPS_OTEL_PROMETHEUS_ENDPOINT: 0.0.0.0:9464
|
||||
STELLAOPS_OTEL_REQUIRE_CLIENT_CERT: "true"
|
||||
STELLAOPS_TENANT_ID: dev
|
||||
STELLAOPS_TEMPO_ENDPOINT: https://stellaops-tempo:3200
|
||||
STELLAOPS_TEMPO_TLS_CERT_FILE: /etc/otel-collector/tls/client.crt
|
||||
STELLAOPS_TEMPO_TLS_KEY_FILE: /etc/otel-collector/tls/client.key
|
||||
STELLAOPS_TEMPO_TLS_CA_FILE: /etc/otel-collector/tls/ca.crt
|
||||
STELLAOPS_LOKI_ENDPOINT: https://stellaops-loki:3100/loki/api/v1/push
|
||||
STELLAOPS_LOKI_TLS_CERT_FILE: /etc/otel-collector/tls/client.crt
|
||||
STELLAOPS_LOKI_TLS_KEY_FILE: /etc/otel-collector/tls/client.key
|
||||
STELLAOPS_LOKI_TLS_CA_FILE: /etc/otel-collector/tls/ca.crt
|
||||
volumes:
|
||||
- ../telemetry/otel-collector-config.yaml:/etc/otel-collector/config.yaml:ro
|
||||
- ../telemetry/certs:/etc/otel-collector/tls:ro
|
||||
ports:
|
||||
- "4317:4317" # OTLP gRPC (mTLS)
|
||||
- "4318:4318" # OTLP HTTP (mTLS)
|
||||
- "9464:9464" # Prometheus exporter (mTLS)
|
||||
- "13133:13133" # Health check
|
||||
- "1777:1777" # pprof
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-fsk", "--cert", "/etc/otel-collector/tls/client.crt", "--key", "/etc/otel-collector/tls/client.key", "--cacert", "/etc/otel-collector/tls/ca.crt", "https://localhost:13133/healthz"]
|
||||
interval: 30s
|
||||
start_period: 15s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
|
||||
networks:
|
||||
default:
|
||||
name: stellaops-telemetry
|
||||
91
devops/compose/env/airgap.env.example
vendored
Normal file
91
devops/compose/env/airgap.env.example
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
# Substitutions for docker-compose.airgap.yaml
|
||||
|
||||
# PostgreSQL Database
|
||||
POSTGRES_USER=stellaops
|
||||
POSTGRES_PASSWORD=airgap-postgres-password
|
||||
POSTGRES_DB=stellaops_platform
|
||||
POSTGRES_PORT=25432
|
||||
|
||||
# Valkey (Redis-compatible cache and messaging)
|
||||
VALKEY_PORT=26379
|
||||
|
||||
# RustFS Object Storage
|
||||
RUSTFS_HTTP_PORT=8080
|
||||
|
||||
# Authority (OAuth2/OIDC)
|
||||
AUTHORITY_ISSUER=https://authority.airgap.local
|
||||
AUTHORITY_PORT=8440
|
||||
AUTHORITY_OFFLINE_CACHE_TOLERANCE=00:45:00
|
||||
|
||||
# Signer
|
||||
SIGNER_POE_INTROSPECT_URL=file:///offline/poe/introspect.json
|
||||
SIGNER_PORT=8441
|
||||
|
||||
# Attestor
|
||||
ATTESTOR_PORT=8442
|
||||
|
||||
# Issuer Directory
|
||||
ISSUER_DIRECTORY_PORT=8447
|
||||
ISSUER_DIRECTORY_SEED_CSAF=true
|
||||
|
||||
# Concelier
|
||||
CONCELIER_PORT=8445
|
||||
|
||||
# Scanner
|
||||
SCANNER_WEB_PORT=8444
|
||||
SCANNER_QUEUE_BROKER=valkey://valkey:6379
|
||||
SCANNER_EVENTS_ENABLED=false
|
||||
SCANNER_EVENTS_DRIVER=valkey
|
||||
SCANNER_EVENTS_DSN=
|
||||
SCANNER_EVENTS_STREAM=stella.events
|
||||
SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS=5
|
||||
SCANNER_EVENTS_MAX_STREAM_LENGTH=10000
|
||||
|
||||
# Surface.Env configuration
|
||||
SCANNER_SURFACE_FS_ENDPOINT=http://rustfs:8080
|
||||
SCANNER_SURFACE_FS_BUCKET=surface-cache
|
||||
SCANNER_SURFACE_CACHE_ROOT=/var/lib/stellaops/surface
|
||||
SCANNER_SURFACE_CACHE_QUOTA_MB=4096
|
||||
SCANNER_SURFACE_PREFETCH_ENABLED=false
|
||||
SCANNER_SURFACE_TENANT=default
|
||||
SCANNER_SURFACE_FEATURES=
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER=file
|
||||
SCANNER_SURFACE_SECRETS_NAMESPACE=
|
||||
SCANNER_SURFACE_SECRETS_ROOT=/etc/stellaops/secrets
|
||||
SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER=
|
||||
SCANNER_SURFACE_SECRETS_ALLOW_INLINE=false
|
||||
SURFACE_SECRETS_HOST_PATH=./offline/surface-secrets
|
||||
|
||||
# Offline Kit configuration
|
||||
SCANNER_OFFLINEKIT_ENABLED=false
|
||||
SCANNER_OFFLINEKIT_REQUIREDSSE=true
|
||||
SCANNER_OFFLINEKIT_REKOROFFLINEMODE=true
|
||||
SCANNER_OFFLINEKIT_TRUSTROOTDIRECTORY=/etc/stellaops/trust-roots
|
||||
SCANNER_OFFLINEKIT_REKORSNAPSHOTDIRECTORY=/var/lib/stellaops/rekor-snapshot
|
||||
SCANNER_OFFLINEKIT_TRUSTROOTS_HOST_PATH=./offline/trust-roots
|
||||
SCANNER_OFFLINEKIT_REKOR_SNAPSHOT_HOST_PATH=./offline/rekor-snapshot
|
||||
|
||||
# Zastava inherits Scanner defaults; override if Observer/Webhook diverge
|
||||
ZASTAVA_SURFACE_FS_ENDPOINT=${SCANNER_SURFACE_FS_ENDPOINT}
|
||||
ZASTAVA_SURFACE_CACHE_ROOT=${SCANNER_SURFACE_CACHE_ROOT}
|
||||
|
||||
# Scheduler
|
||||
SCHEDULER_QUEUE_KIND=Valkey
|
||||
SCHEDULER_QUEUE_VALKEY_URL=valkey:6379
|
||||
SCHEDULER_SCANNER_BASEADDRESS=http://scanner-web:8444
|
||||
|
||||
# Notify
|
||||
NOTIFY_WEB_PORT=9446
|
||||
|
||||
# Advisory AI
|
||||
ADVISORY_AI_WEB_PORT=8448
|
||||
ADVISORY_AI_SBOM_BASEADDRESS=http://scanner-web:8444
|
||||
ADVISORY_AI_INFERENCE_MODE=Local
|
||||
ADVISORY_AI_REMOTE_BASEADDRESS=
|
||||
ADVISORY_AI_REMOTE_APIKEY=
|
||||
|
||||
# Web UI
|
||||
UI_PORT=9443
|
||||
|
||||
# NATS
|
||||
NATS_CLIENT_PORT=24222
|
||||
118
devops/compose/env/cas.env.example
vendored
Normal file
118
devops/compose/env/cas.env.example
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
# CAS (Content Addressable Storage) Environment Configuration
|
||||
# Copy to .env and customize for your deployment
|
||||
#
|
||||
# Aligned with best-in-class vulnerability scanner retention policies:
|
||||
# - Trivy: 7 days vulnerability DB
|
||||
# - Grype: 5 days DB, configurable
|
||||
# - Anchore Enterprise: 90-365 days typical
|
||||
# - Snyk Enterprise: 365 days
|
||||
|
||||
# =============================================================================
|
||||
# DATA PATHS (ensure directories exist with proper permissions)
|
||||
# =============================================================================
|
||||
CAS_DATA_PATH=/var/lib/stellaops/cas
|
||||
CAS_EVIDENCE_PATH=/var/lib/stellaops/evidence
|
||||
CAS_ATTESTATION_PATH=/var/lib/stellaops/attestations
|
||||
|
||||
# =============================================================================
|
||||
# RUSTFS CONFIGURATION
|
||||
# =============================================================================
|
||||
RUSTFS_LOG_LEVEL=info
|
||||
RUSTFS_COMPRESSION=zstd
|
||||
RUSTFS_COMPRESSION_LEVEL=3
|
||||
|
||||
# =============================================================================
|
||||
# PORTS
|
||||
# =============================================================================
|
||||
RUSTFS_CAS_PORT=8180
|
||||
RUSTFS_EVIDENCE_PORT=8181
|
||||
RUSTFS_ATTESTATION_PORT=8182
|
||||
|
||||
# =============================================================================
|
||||
# ACCESS CONTROL - API KEYS
|
||||
# IMPORTANT: Change these in production!
|
||||
# =============================================================================
|
||||
|
||||
# CAS Storage (mutable, lifecycle-managed)
|
||||
RUSTFS_CAS_API_KEY=cas-api-key-CHANGE-IN-PRODUCTION
|
||||
RUSTFS_CAS_READONLY_KEY=cas-readonly-key-CHANGE-IN-PRODUCTION
|
||||
|
||||
# Evidence Storage (immutable)
|
||||
RUSTFS_EVIDENCE_API_KEY=evidence-api-key-CHANGE-IN-PRODUCTION
|
||||
RUSTFS_EVIDENCE_READONLY_KEY=evidence-readonly-key-CHANGE-IN-PRODUCTION
|
||||
|
||||
# Attestation Storage (immutable)
|
||||
RUSTFS_ATTESTATION_API_KEY=attestation-api-key-CHANGE-IN-PRODUCTION
|
||||
RUSTFS_ATTESTATION_READONLY_KEY=attestation-readonly-key-CHANGE-IN-PRODUCTION
|
||||
|
||||
# =============================================================================
|
||||
# SERVICE ACCOUNT KEYS
|
||||
# Each service has its own key for fine-grained access control
|
||||
# IMPORTANT: Generate unique keys per environment!
|
||||
# =============================================================================
|
||||
|
||||
# Scanner service - access to scanner artifacts, surface cache, runtime facts
|
||||
RUSTFS_SCANNER_KEY=scanner-svc-key-GENERATE-UNIQUE
|
||||
# Bucket access: scanner-artifacts (rw), surface-cache (rw), runtime-facts (rw)
|
||||
|
||||
# Signals service - access to runtime facts, signals data, provenance feed
|
||||
RUSTFS_SIGNALS_KEY=signals-svc-key-GENERATE-UNIQUE
|
||||
# Bucket access: runtime-facts (rw), signals-data (rw), provenance-feed (rw)
|
||||
|
||||
# Replay service - access to replay bundles, inputs lock files
|
||||
RUSTFS_REPLAY_KEY=replay-svc-key-GENERATE-UNIQUE
|
||||
# Bucket access: replay-bundles (rw), inputs-lock (rw)
|
||||
|
||||
# Ledger service - access to evidence bundles, merkle roots, hash chains
|
||||
RUSTFS_LEDGER_KEY=ledger-svc-key-GENERATE-UNIQUE
|
||||
# Bucket access: evidence-bundles (rw), merkle-roots (rw), hash-chains (rw)
|
||||
|
||||
# Exporter service - read-only access to evidence bundles
|
||||
RUSTFS_EXPORTER_KEY=exporter-svc-key-GENERATE-UNIQUE
|
||||
# Bucket access: evidence-bundles (r)
|
||||
|
||||
# Attestor service - access to attestations, DSSE envelopes, Rekor receipts
|
||||
RUSTFS_ATTESTOR_KEY=attestor-svc-key-GENERATE-UNIQUE
|
||||
# Bucket access: attestations (rw), dsse-envelopes (rw), rekor-receipts (rw)
|
||||
|
||||
# Verifier service - read-only access to attestations
|
||||
RUSTFS_VERIFIER_KEY=verifier-svc-key-GENERATE-UNIQUE
|
||||
# Bucket access: attestations (r), dsse-envelopes (r), rekor-receipts (r)
|
||||
|
||||
# Global read-only key (for debugging/auditing)
|
||||
RUSTFS_READONLY_KEY=readonly-global-key-GENERATE-UNIQUE
|
||||
# Bucket access: * (r)
|
||||
|
||||
# =============================================================================
|
||||
# LIFECYCLE MANAGEMENT
|
||||
# =============================================================================
|
||||
# Cron schedule for retention policy enforcement (default: 3 AM daily)
|
||||
LIFECYCLE_CRON=0 3 * * *
|
||||
LIFECYCLE_TELEMETRY=true
|
||||
|
||||
# =============================================================================
|
||||
# RETENTION POLICIES (days, 0 = indefinite)
|
||||
# Aligned with enterprise vulnerability scanner best practices
|
||||
# =============================================================================
|
||||
# Vulnerability DB: 7 days (matches Trivy default, Grype uses 5)
|
||||
CAS_RETENTION_VULNERABILITY_DB_DAYS=7
|
||||
|
||||
# SBOM artifacts: 365 days (audit compliance - SOC2, ISO27001, FedRAMP)
|
||||
CAS_RETENTION_SBOM_ARTIFACTS_DAYS=365
|
||||
|
||||
# Scan results: 90 days (common compliance window)
|
||||
CAS_RETENTION_SCAN_RESULTS_DAYS=90
|
||||
|
||||
# Evidence bundles: indefinite (content-addressed, immutable, audit trail)
|
||||
CAS_RETENTION_EVIDENCE_BUNDLES_DAYS=0
|
||||
|
||||
# Attestations: indefinite (signed, immutable, verifiable)
|
||||
CAS_RETENTION_ATTESTATIONS_DAYS=0
|
||||
|
||||
# Temporary artifacts: 1 day (work-in-progress, intermediate files)
|
||||
CAS_RETENTION_TEMP_ARTIFACTS_DAYS=1
|
||||
|
||||
# =============================================================================
|
||||
# TELEMETRY (optional)
|
||||
# =============================================================================
|
||||
OTLP_ENDPOINT=
|
||||
78
devops/compose/env/dev.env.example
vendored
Normal file
78
devops/compose/env/dev.env.example
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
# Substitutions for docker-compose.dev.yaml
|
||||
|
||||
# PostgreSQL Database
|
||||
POSTGRES_USER=stellaops
|
||||
POSTGRES_PASSWORD=dev-postgres-password
|
||||
POSTGRES_DB=stellaops_platform
|
||||
POSTGRES_PORT=5432
|
||||
|
||||
# Valkey (Redis-compatible cache and messaging)
|
||||
VALKEY_PORT=6379
|
||||
|
||||
# RustFS Object Storage
|
||||
RUSTFS_HTTP_PORT=8080
|
||||
|
||||
# Authority (OAuth2/OIDC)
|
||||
AUTHORITY_ISSUER=https://authority.localtest.me
|
||||
AUTHORITY_PORT=8440
|
||||
|
||||
# Signer
|
||||
SIGNER_POE_INTROSPECT_URL=https://licensing.svc.local/introspect
|
||||
SIGNER_PORT=8441
|
||||
|
||||
# Attestor
|
||||
ATTESTOR_PORT=8442
|
||||
|
||||
# Issuer Directory
|
||||
ISSUER_DIRECTORY_PORT=8447
|
||||
ISSUER_DIRECTORY_SEED_CSAF=true
|
||||
|
||||
# Concelier
|
||||
CONCELIER_PORT=8445
|
||||
|
||||
# Scanner
|
||||
SCANNER_WEB_PORT=8444
|
||||
SCANNER_QUEUE_BROKER=nats://nats:4222
|
||||
SCANNER_EVENTS_ENABLED=false
|
||||
SCANNER_EVENTS_DRIVER=valkey
|
||||
SCANNER_EVENTS_DSN=valkey:6379
|
||||
SCANNER_EVENTS_STREAM=stella.events
|
||||
SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS=5
|
||||
SCANNER_EVENTS_MAX_STREAM_LENGTH=10000
|
||||
|
||||
# Surface.Env defaults keep worker/web service aligned with local RustFS and inline secrets
|
||||
SCANNER_SURFACE_FS_ENDPOINT=http://rustfs:8080/api/v1
|
||||
SCANNER_SURFACE_CACHE_ROOT=/var/lib/stellaops/surface
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER=inline
|
||||
SCANNER_SURFACE_SECRETS_ROOT=
|
||||
|
||||
# Zastava inherits Scanner defaults; override if Observer/Webhook diverge
|
||||
ZASTAVA_SURFACE_FS_ENDPOINT=${SCANNER_SURFACE_FS_ENDPOINT}
|
||||
ZASTAVA_SURFACE_CACHE_ROOT=${SCANNER_SURFACE_CACHE_ROOT}
|
||||
ZASTAVA_SURFACE_SECRETS_PROVIDER=${SCANNER_SURFACE_SECRETS_PROVIDER}
|
||||
ZASTAVA_SURFACE_SECRETS_ROOT=${SCANNER_SURFACE_SECRETS_ROOT}
|
||||
|
||||
# Scheduler
|
||||
SCHEDULER_QUEUE_KIND=Nats
|
||||
SCHEDULER_QUEUE_NATS_URL=nats://nats:4222
|
||||
SCHEDULER_SCANNER_BASEADDRESS=http://scanner-web:8444
|
||||
|
||||
# Notify
|
||||
NOTIFY_WEB_PORT=8446
|
||||
|
||||
# Advisory AI
|
||||
ADVISORY_AI_WEB_PORT=8448
|
||||
ADVISORY_AI_SBOM_BASEADDRESS=http://scanner-web:8444
|
||||
ADVISORY_AI_INFERENCE_MODE=Local
|
||||
ADVISORY_AI_REMOTE_BASEADDRESS=
|
||||
ADVISORY_AI_REMOTE_APIKEY=
|
||||
|
||||
# Web UI
|
||||
UI_PORT=8443
|
||||
|
||||
# NATS
|
||||
NATS_CLIENT_PORT=4222
|
||||
|
||||
# CryptoPro (optional)
|
||||
CRYPTOPRO_PORT=18080
|
||||
CRYPTOPRO_ACCEPT_EULA=0
|
||||
64
devops/compose/env/mirror.env.example
vendored
Normal file
64
devops/compose/env/mirror.env.example
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
# Managed mirror profile substitutions
|
||||
|
||||
# Core infrastructure credentials
|
||||
MONGO_INITDB_ROOT_USERNAME=stellaops_mirror
|
||||
MONGO_INITDB_ROOT_PASSWORD=mirror-password
|
||||
MINIO_ROOT_USER=stellaops-mirror
|
||||
MINIO_ROOT_PASSWORD=mirror-minio-secret
|
||||
RUSTFS_HTTP_PORT=8080
|
||||
|
||||
# Scanner surface integration
|
||||
SCANNER_SURFACE_FS_ENDPOINT=http://rustfs:8080/api/v1
|
||||
SCANNER_SURFACE_CACHE_ROOT=/var/lib/stellaops/surface
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER=file
|
||||
SCANNER_SURFACE_SECRETS_ROOT=/etc/stellaops/secrets
|
||||
|
||||
# Mirror HTTP listeners
|
||||
MIRROR_GATEWAY_HTTP_PORT=8080
|
||||
MIRROR_GATEWAY_HTTPS_PORT=9443
|
||||
|
||||
# Concelier mirror configuration
|
||||
CONCELIER_MIRROR_LATEST_SEGMENT=latest
|
||||
CONCELIER_MIRROR_DIRECTORY_SEGMENT=mirror
|
||||
CONCELIER_MIRROR_REQUIRE_AUTH=true
|
||||
CONCELIER_MIRROR_INDEX_BUDGET=600
|
||||
CONCELIER_MIRROR_DOMAIN_PRIMARY_ID=primary
|
||||
CONCELIER_MIRROR_DOMAIN_PRIMARY_NAME=Primary Mirror
|
||||
CONCELIER_MIRROR_DOMAIN_PRIMARY_AUTH=true
|
||||
CONCELIER_MIRROR_DOMAIN_PRIMARY_DOWNLOAD_BUDGET=3600
|
||||
CONCELIER_MIRROR_DOMAIN_SECONDARY_ID=community
|
||||
CONCELIER_MIRROR_DOMAIN_SECONDARY_NAME=Community Mirror
|
||||
CONCELIER_MIRROR_DOMAIN_SECONDARY_AUTH=false
|
||||
CONCELIER_MIRROR_DOMAIN_SECONDARY_DOWNLOAD_BUDGET=1800
|
||||
|
||||
# Authority integration (tokens issued by production Authority)
|
||||
CONCELIER_AUTHORITY_ENABLED=true
|
||||
CONCELIER_AUTHORITY_ALLOW_ANON=false
|
||||
CONCELIER_AUTHORITY_ISSUER=https://authority.stella-ops.org
|
||||
CONCELIER_AUTHORITY_METADATA=
|
||||
CONCELIER_AUTHORITY_CLIENT_ID=stellaops-concelier-mirror
|
||||
CONCELIER_AUTHORITY_SCOPE=concelier.mirror.read
|
||||
CONCELIER_AUTHORITY_AUDIENCE=api://concelier.mirror
|
||||
|
||||
# Excititor mirror configuration
|
||||
EXCITITOR_MONGO_DATABASE=excititor
|
||||
EXCITITOR_FILESYSTEM_OVERWRITE=false
|
||||
EXCITITOR_MIRROR_DOMAIN_PRIMARY_ID=primary
|
||||
EXCITITOR_MIRROR_DOMAIN_PRIMARY_NAME=Primary Mirror
|
||||
EXCITITOR_MIRROR_DOMAIN_PRIMARY_AUTH=true
|
||||
EXCITITOR_MIRROR_DOMAIN_PRIMARY_INDEX_BUDGET=300
|
||||
EXCITITOR_MIRROR_DOMAIN_PRIMARY_DOWNLOAD_BUDGET=2400
|
||||
EXCITITOR_MIRROR_PRIMARY_EXPORT_CONSENSUS_KEY=consensus-json
|
||||
EXCITITOR_MIRROR_PRIMARY_EXPORT_CONSENSUS_FORMAT=json
|
||||
EXCITITOR_MIRROR_PRIMARY_EXPORT_CONSENSUS_VIEW=consensus
|
||||
EXCITITOR_MIRROR_PRIMARY_EXPORT_OPENVEX_KEY=consensus-openvex
|
||||
EXCITITOR_MIRROR_PRIMARY_EXPORT_OPENVEX_FORMAT=openvex
|
||||
EXCITITOR_MIRROR_PRIMARY_EXPORT_OPENVEX_VIEW=consensus
|
||||
EXCITITOR_MIRROR_DOMAIN_SECONDARY_ID=community
|
||||
EXCITITOR_MIRROR_DOMAIN_SECONDARY_NAME=Community Mirror
|
||||
EXCITITOR_MIRROR_DOMAIN_SECONDARY_AUTH=false
|
||||
EXCITITOR_MIRROR_DOMAIN_SECONDARY_INDEX_BUDGET=120
|
||||
EXCITITOR_MIRROR_DOMAIN_SECONDARY_DOWNLOAD_BUDGET=600
|
||||
EXCITITOR_MIRROR_SECONDARY_EXPORT_KEY=community-consensus
|
||||
EXCITITOR_MIRROR_SECONDARY_EXPORT_FORMAT=json
|
||||
EXCITITOR_MIRROR_SECONDARY_EXPORT_VIEW=consensus
|
||||
12
devops/compose/env/mock.env.example
vendored
Normal file
12
devops/compose/env/mock.env.example
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# Dev-only overlay env for docker-compose.mock.yaml
|
||||
# Use together with dev.env.example:
|
||||
# docker compose --env-file env/dev.env.example --env-file env/mock.env.example -f docker-compose.dev.yaml -f docker-compose.mock.yaml config
|
||||
|
||||
# Optional: override ports if you expose mock services
|
||||
ORCHESTRATOR_PORT=8450
|
||||
POLICY_REGISTRY_PORT=8451
|
||||
VEX_LENS_PORT=8452
|
||||
FINDINGS_LEDGER_PORT=8453
|
||||
VULN_EXPLORER_API_PORT=8454
|
||||
PACKS_REGISTRY_PORT=8455
|
||||
TASK_RUNNER_PORT=8456
|
||||
96
devops/compose/env/prod.env.example
vendored
Normal file
96
devops/compose/env/prod.env.example
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
# Substitutions for docker-compose.prod.yaml
|
||||
# WARNING: Replace all placeholder secrets with values sourced from your secret manager.
|
||||
|
||||
# PostgreSQL Database
|
||||
POSTGRES_USER=stellaops-prod
|
||||
POSTGRES_PASSWORD=REPLACE_WITH_STRONG_PASSWORD
|
||||
POSTGRES_DB=stellaops_platform
|
||||
POSTGRES_PORT=5432
|
||||
|
||||
# Valkey (Redis-compatible cache and messaging)
|
||||
VALKEY_PORT=6379
|
||||
|
||||
# RustFS Object Storage
|
||||
RUSTFS_HTTP_PORT=8080
|
||||
|
||||
# Authority (OAuth2/OIDC)
|
||||
AUTHORITY_ISSUER=https://authority.prod.stella-ops.org
|
||||
AUTHORITY_PORT=8440
|
||||
AUTHORITY_OFFLINE_CACHE_TOLERANCE=00:30:00
|
||||
|
||||
# Signer
|
||||
SIGNER_POE_INTROSPECT_URL=https://licensing.prod.stella-ops.org/introspect
|
||||
SIGNER_PORT=8441
|
||||
|
||||
# Attestor
|
||||
ATTESTOR_PORT=8442
|
||||
|
||||
# Issuer Directory
|
||||
ISSUER_DIRECTORY_PORT=8447
|
||||
ISSUER_DIRECTORY_SEED_CSAF=true
|
||||
|
||||
# Concelier
|
||||
CONCELIER_PORT=8445
|
||||
|
||||
# Scanner
|
||||
SCANNER_WEB_PORT=8444
|
||||
SCANNER_QUEUE_BROKER=valkey://valkey:6379
|
||||
# `true` enables signed scanner events for Notify ingestion.
|
||||
SCANNER_EVENTS_ENABLED=true
|
||||
SCANNER_EVENTS_DRIVER=valkey
|
||||
SCANNER_EVENTS_DSN=
|
||||
SCANNER_EVENTS_STREAM=stella.events
|
||||
SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS=5
|
||||
SCANNER_EVENTS_MAX_STREAM_LENGTH=10000
|
||||
|
||||
# Surface.Env configuration
|
||||
SCANNER_SURFACE_FS_ENDPOINT=https://surfacefs.prod.stella-ops.org/api/v1
|
||||
SCANNER_SURFACE_FS_BUCKET=surface-cache
|
||||
SCANNER_SURFACE_CACHE_ROOT=/var/lib/stellaops/surface
|
||||
SCANNER_SURFACE_CACHE_QUOTA_MB=4096
|
||||
SCANNER_SURFACE_PREFETCH_ENABLED=false
|
||||
SCANNER_SURFACE_TENANT=default
|
||||
SCANNER_SURFACE_FEATURES=
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER=kubernetes
|
||||
SCANNER_SURFACE_SECRETS_NAMESPACE=
|
||||
SCANNER_SURFACE_SECRETS_ROOT=stellaops/scanner
|
||||
SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER=
|
||||
SCANNER_SURFACE_SECRETS_ALLOW_INLINE=false
|
||||
SURFACE_SECRETS_HOST_PATH=./offline/surface-secrets
|
||||
|
||||
# Offline Kit configuration
|
||||
SCANNER_OFFLINEKIT_ENABLED=false
|
||||
SCANNER_OFFLINEKIT_REQUIREDSSE=true
|
||||
SCANNER_OFFLINEKIT_REKOROFFLINEMODE=true
|
||||
SCANNER_OFFLINEKIT_TRUSTROOTDIRECTORY=/etc/stellaops/trust-roots
|
||||
SCANNER_OFFLINEKIT_REKORSNAPSHOTDIRECTORY=/var/lib/stellaops/rekor-snapshot
|
||||
SCANNER_OFFLINEKIT_TRUSTROOTS_HOST_PATH=./offline/trust-roots
|
||||
SCANNER_OFFLINEKIT_REKOR_SNAPSHOT_HOST_PATH=./offline/rekor-snapshot
|
||||
|
||||
# Zastava inherits Scanner defaults; override if Observer/Webhook diverge
|
||||
ZASTAVA_SURFACE_FS_ENDPOINT=${SCANNER_SURFACE_FS_ENDPOINT}
|
||||
ZASTAVA_SURFACE_CACHE_ROOT=${SCANNER_SURFACE_CACHE_ROOT}
|
||||
|
||||
# Scheduler
|
||||
SCHEDULER_QUEUE_KIND=Valkey
|
||||
SCHEDULER_QUEUE_VALKEY_URL=valkey:6379
|
||||
SCHEDULER_SCANNER_BASEADDRESS=http://scanner-web:8444
|
||||
|
||||
# Notify
|
||||
NOTIFY_WEB_PORT=8446
|
||||
|
||||
# Advisory AI
|
||||
ADVISORY_AI_WEB_PORT=8448
|
||||
ADVISORY_AI_SBOM_BASEADDRESS=https://scanner-web:8444
|
||||
ADVISORY_AI_INFERENCE_MODE=Local
|
||||
ADVISORY_AI_REMOTE_BASEADDRESS=
|
||||
ADVISORY_AI_REMOTE_APIKEY=
|
||||
|
||||
# Web UI
|
||||
UI_PORT=8443
|
||||
|
||||
# NATS
|
||||
NATS_CLIENT_PORT=4222
|
||||
|
||||
# External reverse proxy (Traefik, Envoy, etc.) that terminates TLS.
|
||||
FRONTDOOR_NETWORK=stellaops_frontdoor
|
||||
91
devops/compose/env/stage.env.example
vendored
Normal file
91
devops/compose/env/stage.env.example
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
# Substitutions for docker-compose.stage.yaml
|
||||
|
||||
# PostgreSQL Database
|
||||
POSTGRES_USER=stellaops
|
||||
POSTGRES_PASSWORD=stage-postgres-password
|
||||
POSTGRES_DB=stellaops_platform
|
||||
POSTGRES_PORT=5432
|
||||
|
||||
# Valkey (Redis-compatible cache and messaging)
|
||||
VALKEY_PORT=6379
|
||||
|
||||
# RustFS Object Storage
|
||||
RUSTFS_HTTP_PORT=8080
|
||||
|
||||
# Authority (OAuth2/OIDC)
|
||||
AUTHORITY_ISSUER=https://authority.stage.stella-ops.internal
|
||||
AUTHORITY_PORT=8440
|
||||
AUTHORITY_OFFLINE_CACHE_TOLERANCE=00:30:00
|
||||
|
||||
# Signer
|
||||
SIGNER_POE_INTROSPECT_URL=https://licensing.stage.stella-ops.internal/introspect
|
||||
SIGNER_PORT=8441
|
||||
|
||||
# Attestor
|
||||
ATTESTOR_PORT=8442
|
||||
|
||||
# Issuer Directory
|
||||
ISSUER_DIRECTORY_PORT=8447
|
||||
ISSUER_DIRECTORY_SEED_CSAF=true
|
||||
|
||||
# Concelier
|
||||
CONCELIER_PORT=8445
|
||||
|
||||
# Scanner
|
||||
SCANNER_WEB_PORT=8444
|
||||
SCANNER_QUEUE_BROKER=valkey://valkey:6379
|
||||
SCANNER_EVENTS_ENABLED=false
|
||||
SCANNER_EVENTS_DRIVER=valkey
|
||||
SCANNER_EVENTS_DSN=
|
||||
SCANNER_EVENTS_STREAM=stella.events
|
||||
SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS=5
|
||||
SCANNER_EVENTS_MAX_STREAM_LENGTH=10000
|
||||
|
||||
# Surface.Env configuration
|
||||
SCANNER_SURFACE_FS_ENDPOINT=http://rustfs:8080
|
||||
SCANNER_SURFACE_FS_BUCKET=surface-cache
|
||||
SCANNER_SURFACE_CACHE_ROOT=/var/lib/stellaops/surface
|
||||
SCANNER_SURFACE_CACHE_QUOTA_MB=4096
|
||||
SCANNER_SURFACE_PREFETCH_ENABLED=false
|
||||
SCANNER_SURFACE_TENANT=default
|
||||
SCANNER_SURFACE_FEATURES=
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER=kubernetes
|
||||
SCANNER_SURFACE_SECRETS_NAMESPACE=
|
||||
SCANNER_SURFACE_SECRETS_ROOT=stellaops/scanner
|
||||
SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER=
|
||||
SCANNER_SURFACE_SECRETS_ALLOW_INLINE=false
|
||||
SURFACE_SECRETS_HOST_PATH=./offline/surface-secrets
|
||||
|
||||
# Offline Kit configuration
|
||||
SCANNER_OFFLINEKIT_ENABLED=false
|
||||
SCANNER_OFFLINEKIT_REQUIREDSSE=true
|
||||
SCANNER_OFFLINEKIT_REKOROFFLINEMODE=true
|
||||
SCANNER_OFFLINEKIT_TRUSTROOTDIRECTORY=/etc/stellaops/trust-roots
|
||||
SCANNER_OFFLINEKIT_REKORSNAPSHOTDIRECTORY=/var/lib/stellaops/rekor-snapshot
|
||||
SCANNER_OFFLINEKIT_TRUSTROOTS_HOST_PATH=./offline/trust-roots
|
||||
SCANNER_OFFLINEKIT_REKOR_SNAPSHOT_HOST_PATH=./offline/rekor-snapshot
|
||||
|
||||
# Zastava inherits Scanner defaults; override if Observer/Webhook diverge
|
||||
ZASTAVA_SURFACE_FS_ENDPOINT=${SCANNER_SURFACE_FS_ENDPOINT}
|
||||
ZASTAVA_SURFACE_CACHE_ROOT=${SCANNER_SURFACE_CACHE_ROOT}
|
||||
|
||||
# Scheduler
|
||||
SCHEDULER_QUEUE_KIND=Valkey
|
||||
SCHEDULER_QUEUE_VALKEY_URL=valkey:6379
|
||||
SCHEDULER_SCANNER_BASEADDRESS=http://scanner-web:8444
|
||||
|
||||
# Notify
|
||||
NOTIFY_WEB_PORT=8446
|
||||
|
||||
# Advisory AI
|
||||
ADVISORY_AI_WEB_PORT=8448
|
||||
ADVISORY_AI_SBOM_BASEADDRESS=http://scanner-web:8444
|
||||
ADVISORY_AI_INFERENCE_MODE=Local
|
||||
ADVISORY_AI_REMOTE_BASEADDRESS=
|
||||
ADVISORY_AI_REMOTE_APIKEY=
|
||||
|
||||
# Web UI
|
||||
UI_PORT=8443
|
||||
|
||||
# NATS
|
||||
NATS_CLIENT_PORT=4222
|
||||
0
devops/compose/mirror-data/concelier/.gitkeep
Normal file
0
devops/compose/mirror-data/concelier/.gitkeep
Normal file
0
devops/compose/mirror-data/excititor/.gitkeep
Normal file
0
devops/compose/mirror-data/excititor/.gitkeep
Normal file
13
devops/compose/mirror-gateway/README.md
Normal file
13
devops/compose/mirror-gateway/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Mirror Gateway Assets
|
||||
|
||||
This directory holds the reverse-proxy configuration and TLS material for the managed
|
||||
mirror profile:
|
||||
|
||||
- `conf.d/*.conf` – nginx configuration shipped with the profile.
|
||||
- `tls/` – place environment-specific certificates and private keys
|
||||
(`mirror-primary.{crt,key}`, `mirror-community.{crt,key}`, etc.).
|
||||
- `secrets/` – populate Basic Auth credential stores (`*.htpasswd`) that gate each
|
||||
mirror domain. Generate with `htpasswd -B`.
|
||||
|
||||
The Compose bundle mounts these paths read-only. Populate `tls/` with the actual
|
||||
certificates before invoking `docker compose config` or `docker compose up`.
|
||||
44
devops/compose/mirror-gateway/conf.d/mirror-locations.conf
Normal file
44
devops/compose/mirror-gateway/conf.d/mirror-locations.conf
Normal file
@@ -0,0 +1,44 @@
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_redirect off;
|
||||
|
||||
add_header X-Cache-Status $upstream_cache_status always;
|
||||
|
||||
location = /healthz {
|
||||
default_type application/json;
|
||||
return 200 '{"status":"ok"}';
|
||||
}
|
||||
|
||||
location /concelier/exports/ {
|
||||
proxy_pass http://concelier_backend/concelier/exports/;
|
||||
proxy_cache mirror_cache;
|
||||
proxy_cache_key $mirror_cache_key;
|
||||
proxy_cache_valid 200 5m;
|
||||
proxy_cache_valid 404 1m;
|
||||
add_header Cache-Control "public, max-age=300, immutable" always;
|
||||
}
|
||||
|
||||
location /concelier/ {
|
||||
proxy_pass http://concelier_backend/concelier/;
|
||||
proxy_cache off;
|
||||
}
|
||||
|
||||
location /excititor/mirror/ {
|
||||
proxy_pass http://excititor_backend/excititor/mirror/;
|
||||
proxy_cache mirror_cache;
|
||||
proxy_cache_key $mirror_cache_key;
|
||||
proxy_cache_valid 200 5m;
|
||||
proxy_cache_valid 404 1m;
|
||||
add_header Cache-Control "public, max-age=300, immutable" always;
|
||||
}
|
||||
|
||||
location /excititor/ {
|
||||
proxy_pass http://excititor_backend/excititor/;
|
||||
proxy_cache off;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 404;
|
||||
}
|
||||
51
devops/compose/mirror-gateway/conf.d/mirror.conf
Normal file
51
devops/compose/mirror-gateway/conf.d/mirror.conf
Normal file
@@ -0,0 +1,51 @@
|
||||
proxy_cache_path /var/cache/nginx/mirror levels=1:2 keys_zone=mirror_cache:100m max_size=10g inactive=12h use_temp_path=off;
|
||||
|
||||
map $request_uri $mirror_cache_key {
|
||||
default $scheme$request_method$host$request_uri;
|
||||
}
|
||||
|
||||
upstream concelier_backend {
|
||||
server concelier:8445;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
upstream excititor_backend {
|
||||
server excititor:8448;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name mirror-primary.stella-ops.org;
|
||||
|
||||
ssl_certificate /etc/nginx/tls/mirror-primary.crt;
|
||||
ssl_certificate_key /etc/nginx/tls/mirror-primary.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
auth_basic "StellaOps Mirror – primary";
|
||||
auth_basic_user_file /etc/nginx/secrets/mirror-primary.htpasswd;
|
||||
|
||||
include /etc/nginx/conf.d/mirror-locations.conf;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name mirror-community.stella-ops.org;
|
||||
|
||||
ssl_certificate /etc/nginx/tls/mirror-community.crt;
|
||||
ssl_certificate_key /etc/nginx/tls/mirror-community.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
auth_basic "StellaOps Mirror – community";
|
||||
auth_basic_user_file /etc/nginx/secrets/mirror-community.htpasswd;
|
||||
|
||||
include /etc/nginx/conf.d/mirror-locations.conf;
|
||||
}
|
||||
0
devops/compose/mirror-gateway/secrets/.gitkeep
Normal file
0
devops/compose/mirror-gateway/secrets/.gitkeep
Normal file
0
devops/compose/mirror-gateway/tls/.gitkeep
Normal file
0
devops/compose/mirror-gateway/tls/.gitkeep
Normal file
0
devops/compose/mirror-secrets/.gitkeep
Normal file
0
devops/compose/mirror-secrets/.gitkeep
Normal file
33
devops/compose/postgres-init/01-extensions.sql
Normal file
33
devops/compose/postgres-init/01-extensions.sql
Normal file
@@ -0,0 +1,33 @@
|
||||
-- PostgreSQL initialization for StellaOps air-gap deployment
|
||||
-- This script runs automatically on first container start
|
||||
|
||||
-- Enable pg_stat_statements extension for query performance analysis
|
||||
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
|
||||
|
||||
-- Enable other useful extensions
|
||||
CREATE EXTENSION IF NOT EXISTS pg_trgm; -- Fuzzy text search
|
||||
CREATE EXTENSION IF NOT EXISTS btree_gin; -- GIN indexes for scalar types
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto; -- Cryptographic functions
|
||||
|
||||
-- Create schemas for all modules
|
||||
-- Migrations will create tables within these schemas
|
||||
CREATE SCHEMA IF NOT EXISTS authority;
|
||||
CREATE SCHEMA IF NOT EXISTS vuln;
|
||||
CREATE SCHEMA IF NOT EXISTS vex;
|
||||
CREATE SCHEMA IF NOT EXISTS scheduler;
|
||||
CREATE SCHEMA IF NOT EXISTS notify;
|
||||
CREATE SCHEMA IF NOT EXISTS policy;
|
||||
CREATE SCHEMA IF NOT EXISTS concelier;
|
||||
CREATE SCHEMA IF NOT EXISTS audit;
|
||||
CREATE SCHEMA IF NOT EXISTS unknowns;
|
||||
|
||||
-- Grant usage to application user (assumes POSTGRES_USER is the app user)
|
||||
GRANT USAGE ON SCHEMA authority TO PUBLIC;
|
||||
GRANT USAGE ON SCHEMA vuln TO PUBLIC;
|
||||
GRANT USAGE ON SCHEMA vex TO PUBLIC;
|
||||
GRANT USAGE ON SCHEMA scheduler TO PUBLIC;
|
||||
GRANT USAGE ON SCHEMA notify TO PUBLIC;
|
||||
GRANT USAGE ON SCHEMA policy TO PUBLIC;
|
||||
GRANT USAGE ON SCHEMA concelier TO PUBLIC;
|
||||
GRANT USAGE ON SCHEMA audit TO PUBLIC;
|
||||
GRANT USAGE ON SCHEMA unknowns TO PUBLIC;
|
||||
28
devops/compose/scripts/backup.sh
Normal file
28
devops/compose/scripts/backup.sh
Normal file
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "StellaOps Compose Backup"
|
||||
echo "This will create a tar.gz of Mongo, MinIO (object-store), and Redis data volumes."
|
||||
read -rp "Proceed? [y/N] " ans
|
||||
[[ ${ans:-N} =~ ^[Yy]$ ]] || { echo "Aborted."; exit 1; }
|
||||
|
||||
TS=$(date -u +%Y%m%dT%H%M%SZ)
|
||||
OUT_DIR=${BACKUP_DIR:-backups}
|
||||
mkdir -p "$OUT_DIR"
|
||||
|
||||
docker compose ps >/dev/null
|
||||
|
||||
echo "Pausing worker containers for consistency..."
|
||||
docker compose pause scanner-worker scheduler-worker taskrunner-worker || true
|
||||
|
||||
echo "Backing up volumes..."
|
||||
docker run --rm \
|
||||
-v stellaops-mongo:/data/db:ro \
|
||||
-v stellaops-minio:/data/minio:ro \
|
||||
-v stellaops-redis:/data/redis:ro \
|
||||
-v "$PWD/$OUT_DIR":/out \
|
||||
alpine sh -c "cd / && tar czf /out/stellaops-backup-$TS.tar.gz data"
|
||||
|
||||
docker compose unpause scanner-worker scheduler-worker taskrunner-worker || true
|
||||
|
||||
echo "Backup written to $OUT_DIR/stellaops-backup-$TS.tar.gz"
|
||||
25
devops/compose/scripts/quickstart.sh
Normal file
25
devops/compose/scripts/quickstart.sh
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
COMPOSE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
ENV_FILE="${1:-$COMPOSE_DIR/env/dev.env.example}"
|
||||
USE_MOCK="${USE_MOCK:-0}"
|
||||
|
||||
FILES=(-f "$COMPOSE_DIR/docker-compose.dev.yaml")
|
||||
ENV_FILES=(--env-file "$ENV_FILE")
|
||||
|
||||
if [[ "$USE_MOCK" == "1" ]]; then
|
||||
FILES+=(-f "$COMPOSE_DIR/docker-compose.mock.yaml")
|
||||
ENV_FILES+=(--env-file "$COMPOSE_DIR/env/mock.env.example")
|
||||
fi
|
||||
|
||||
echo "Validating compose config..."
|
||||
docker compose "${ENV_FILES[@]}" "${FILES[@]}" config > /tmp/compose-validated.yaml
|
||||
echo "Config written to /tmp/compose-validated.yaml"
|
||||
|
||||
echo "Starting stack..."
|
||||
docker compose "${ENV_FILES[@]}" "${FILES[@]}" up -d
|
||||
|
||||
echo "Stack started. To stop: docker compose ${ENV_FILES[*]} ${FILES[*]} down"
|
||||
15
devops/compose/scripts/reset.sh
Normal file
15
devops/compose/scripts/reset.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "WARNING: This will stop the stack and wipe Mongo, MinIO, and Redis volumes."
|
||||
read -rp "Type 'RESET' to continue: " ans
|
||||
[[ ${ans:-} == "RESET" ]] || { echo "Aborted."; exit 1; }
|
||||
|
||||
docker compose down
|
||||
|
||||
for vol in stellaops-mongo stellaops-minio stellaops-redis; do
|
||||
echo "Removing volume $vol"
|
||||
docker volume rm "$vol" || true
|
||||
done
|
||||
|
||||
echo "Reset complete. Re-run compose with your env file to recreate volumes."
|
||||
31
devops/database/local-postgres/docker-compose.yml
Normal file
31
devops/database/local-postgres/docker-compose.yml
Normal file
@@ -0,0 +1,31 @@
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
stella-postgres:
|
||||
image: postgres:17
|
||||
container_name: stella-postgres
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: stella
|
||||
POSTGRES_PASSWORD: stella
|
||||
POSTGRES_DB: stella
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- stella-postgres-data:/var/lib/postgresql/data
|
||||
- ./init:/docker-entrypoint-initdb.d:ro
|
||||
command:
|
||||
- "postgres"
|
||||
- "-c"
|
||||
- "shared_preload_libraries=pg_stat_statements"
|
||||
- "-c"
|
||||
- "pg_stat_statements.track=all"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
volumes:
|
||||
stella-postgres-data:
|
||||
driver: local
|
||||
17
devops/database/local-postgres/init/01-extensions.sql
Normal file
17
devops/database/local-postgres/init/01-extensions.sql
Normal file
@@ -0,0 +1,17 @@
|
||||
-- Enable pg_stat_statements extension for query performance analysis
|
||||
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
|
||||
|
||||
-- Enable other useful extensions
|
||||
CREATE EXTENSION IF NOT EXISTS pg_trgm; -- Fuzzy text search
|
||||
CREATE EXTENSION IF NOT EXISTS btree_gin; -- GIN indexes for scalar types
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto; -- Cryptographic functions
|
||||
|
||||
-- Create schemas for all modules
|
||||
CREATE SCHEMA IF NOT EXISTS authority;
|
||||
CREATE SCHEMA IF NOT EXISTS vuln;
|
||||
CREATE SCHEMA IF NOT EXISTS vex;
|
||||
CREATE SCHEMA IF NOT EXISTS scheduler;
|
||||
CREATE SCHEMA IF NOT EXISTS notify;
|
||||
CREATE SCHEMA IF NOT EXISTS policy;
|
||||
CREATE SCHEMA IF NOT EXISTS concelier;
|
||||
CREATE SCHEMA IF NOT EXISTS audit;
|
||||
16
devops/database/mongo/indices/README.md
Normal file
16
devops/database/mongo/indices/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# MongoDB Provenance Indexes
|
||||
|
||||
Indexes supporting Sprint 401 reachability/provenance queries.
|
||||
|
||||
## Available indexes
|
||||
- `events_by_subject_kind_provenance`: `(subject.digest.sha256, kind, provenance.dsse.rekor.logIndex)` for subject/kind lookups with Rekor presence.
|
||||
- `events_unproven_by_kind`: `(kind, trust.verified, provenance.dsse.rekor.logIndex)` to find unverified or missing-Rekor events per kind.
|
||||
- `events_by_rekor_logindex`: `(provenance.dsse.rekor.logIndex)` to audit Rekor alignment.
|
||||
|
||||
## Apply
|
||||
```js
|
||||
// From mongo shell (connected to provenance database)
|
||||
load('ops/mongo/indices/events_provenance_indices.js');
|
||||
```
|
||||
|
||||
Indexes are idempotent; rerunning is safe.
|
||||
89
devops/database/mongo/indices/events_provenance_indices.js
Normal file
89
devops/database/mongo/indices/events_provenance_indices.js
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* MongoDB indexes for DSSE provenance queries on the events collection.
|
||||
* Run with: mongosh stellaops_db < events_provenance_indices.js
|
||||
*
|
||||
* These indexes support:
|
||||
* - Proven VEX/SBOM/SCAN lookup by subject digest
|
||||
* - Compliance gap queries (unverified events)
|
||||
* - Rekor log index lookups
|
||||
* - Backfill service queries
|
||||
*
|
||||
* Created: 2025-11-27 (PROV-INDEX-401-030)
|
||||
* C# equivalent: src/StellaOps.Events.Mongo/MongoIndexes.cs
|
||||
*/
|
||||
|
||||
// Switch to the target database (override via --eval "var dbName='custom'" if needed)
|
||||
const targetDb = typeof dbName !== 'undefined' ? dbName : 'stellaops';
|
||||
db = db.getSiblingDB(targetDb);
|
||||
|
||||
print(`Creating provenance indexes on ${targetDb}.events...`);
|
||||
|
||||
// Index 1: Lookup proven events by subject digest + kind
|
||||
db.events.createIndex(
|
||||
{
|
||||
"subject.digest.sha256": 1,
|
||||
"kind": 1,
|
||||
"provenance.dsse.rekor.logIndex": 1
|
||||
},
|
||||
{
|
||||
name: "events_by_subject_kind_provenance",
|
||||
background: true
|
||||
}
|
||||
);
|
||||
print(" - events_by_subject_kind_provenance");
|
||||
|
||||
// Index 2: Find unproven evidence by kind (compliance gap queries)
|
||||
db.events.createIndex(
|
||||
{
|
||||
"kind": 1,
|
||||
"trust.verified": 1,
|
||||
"provenance.dsse.rekor.logIndex": 1
|
||||
},
|
||||
{
|
||||
name: "events_unproven_by_kind",
|
||||
background: true
|
||||
}
|
||||
);
|
||||
print(" - events_unproven_by_kind");
|
||||
|
||||
// Index 3: Direct Rekor log index lookup
|
||||
db.events.createIndex(
|
||||
{
|
||||
"provenance.dsse.rekor.logIndex": 1
|
||||
},
|
||||
{
|
||||
name: "events_by_rekor_logindex",
|
||||
background: true
|
||||
}
|
||||
);
|
||||
print(" - events_by_rekor_logindex");
|
||||
|
||||
// Index 4: Envelope digest lookup (for backfill deduplication)
|
||||
db.events.createIndex(
|
||||
{
|
||||
"provenance.dsse.envelopeDigest": 1
|
||||
},
|
||||
{
|
||||
name: "events_by_envelope_digest",
|
||||
background: true,
|
||||
sparse: true
|
||||
}
|
||||
);
|
||||
print(" - events_by_envelope_digest");
|
||||
|
||||
// Index 5: Timestamp + kind for compliance reporting time ranges
|
||||
db.events.createIndex(
|
||||
{
|
||||
"ts": -1,
|
||||
"kind": 1,
|
||||
"trust.verified": 1
|
||||
},
|
||||
{
|
||||
name: "events_by_ts_kind_verified",
|
||||
background: true
|
||||
}
|
||||
);
|
||||
print(" - events_by_ts_kind_verified");
|
||||
|
||||
print("\nProvenance indexes created successfully.");
|
||||
print("Run 'db.events.getIndexes()' to verify.");
|
||||
67
devops/database/mongo/indices/reachability_store_indices.js
Normal file
67
devops/database/mongo/indices/reachability_store_indices.js
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* MongoDB indexes for the shared reachability store collections used by Signals/Policy/Scanner.
|
||||
* Run with: mongosh stellaops_db < reachability_store_indices.js
|
||||
*
|
||||
* Collections:
|
||||
* - func_nodes: canonical function nodes keyed by graph + symbol ID and joinable by (purl, symbolDigest)
|
||||
* - call_edges: canonical call edges keyed by graph and joinable by (purl, symbolDigest)
|
||||
* - cve_func_hits: per-subject mapping of CVE -> affected/reachable functions with evidence pointers
|
||||
*
|
||||
* Created: 2025-12-13 (SIG-STORE-401-016)
|
||||
*/
|
||||
|
||||
// Switch to the target database (override via --eval "var dbName='custom'" if needed)
|
||||
const targetDb = typeof dbName !== 'undefined' ? dbName : 'stellaops';
|
||||
db = db.getSiblingDB(targetDb);
|
||||
|
||||
print(`Creating reachability store indexes on ${targetDb}...`);
|
||||
|
||||
print(`- func_nodes`);
|
||||
db.func_nodes.createIndex(
|
||||
{ "graphHash": 1, "symbolId": 1 },
|
||||
{ name: "func_nodes_by_graph_symbol", unique: true, background: true }
|
||||
);
|
||||
db.func_nodes.createIndex(
|
||||
{ "purl": 1, "symbolDigest": 1 },
|
||||
{ name: "func_nodes_by_purl_symboldigest", background: true, sparse: true }
|
||||
);
|
||||
db.func_nodes.createIndex(
|
||||
{ "codeId": 1 },
|
||||
{ name: "func_nodes_by_code_id", background: true, sparse: true }
|
||||
);
|
||||
|
||||
print(`- call_edges`);
|
||||
db.call_edges.createIndex(
|
||||
{ "graphHash": 1, "sourceId": 1, "targetId": 1, "type": 1 },
|
||||
{ name: "call_edges_by_graph_edge", unique: true, background: true }
|
||||
);
|
||||
db.call_edges.createIndex(
|
||||
{ "graphHash": 1, "sourceId": 1 },
|
||||
{ name: "call_edges_by_graph_source", background: true }
|
||||
);
|
||||
db.call_edges.createIndex(
|
||||
{ "graphHash": 1, "targetId": 1 },
|
||||
{ name: "call_edges_by_graph_target", background: true }
|
||||
);
|
||||
db.call_edges.createIndex(
|
||||
{ "purl": 1, "symbolDigest": 1 },
|
||||
{ name: "call_edges_by_purl_symboldigest", background: true, sparse: true }
|
||||
);
|
||||
|
||||
print(`- cve_func_hits`);
|
||||
db.cve_func_hits.createIndex(
|
||||
{ "subjectKey": 1, "cveId": 1 },
|
||||
{ name: "cve_func_hits_by_subject_cve", background: true }
|
||||
);
|
||||
db.cve_func_hits.createIndex(
|
||||
{ "cveId": 1, "purl": 1, "symbolDigest": 1 },
|
||||
{ name: "cve_func_hits_by_cve_purl_symboldigest", background: true, sparse: true }
|
||||
);
|
||||
db.cve_func_hits.createIndex(
|
||||
{ "graphHash": 1 },
|
||||
{ name: "cve_func_hits_by_graph", background: true, sparse: true }
|
||||
);
|
||||
|
||||
print("\nReachability store indexes created successfully.");
|
||||
print("Run db.func_nodes.getIndexes(), db.call_edges.getIndexes(), db.cve_func_hits.getIndexes() to verify.");
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
// Task Runner baseline collections and indexes
|
||||
// Mirrors docs/modules/taskrunner/migrations/pack-run-collections.md (last updated 2025-11-06)
|
||||
|
||||
function ensureCollection(name, validator) {
|
||||
const existing = db.getCollectionNames();
|
||||
if (!existing.includes(name)) {
|
||||
db.createCollection(name, { validator, validationLevel: "moderate" });
|
||||
} else if (validator) {
|
||||
db.runCommand({ collMod: name, validator, validationLevel: "moderate" });
|
||||
}
|
||||
}
|
||||
|
||||
const runValidator = {
|
||||
$jsonSchema: {
|
||||
bsonType: "object",
|
||||
required: ["planHash", "plan", "failurePolicy", "requestedAt", "createdAt", "updatedAt", "steps"],
|
||||
properties: {
|
||||
_id: { bsonType: "string" },
|
||||
planHash: { bsonType: "string" },
|
||||
plan: { bsonType: "object" },
|
||||
failurePolicy: { bsonType: "object" },
|
||||
requestedAt: { bsonType: "date" },
|
||||
createdAt: { bsonType: "date" },
|
||||
updatedAt: { bsonType: "date" },
|
||||
steps: {
|
||||
bsonType: "array",
|
||||
items: {
|
||||
bsonType: "object",
|
||||
required: ["stepId", "status", "attempts"],
|
||||
properties: {
|
||||
stepId: { bsonType: "string" },
|
||||
status: { bsonType: "string" },
|
||||
attempts: { bsonType: "int" },
|
||||
kind: { bsonType: "string" },
|
||||
enabled: { bsonType: "bool" },
|
||||
continueOnError: { bsonType: "bool" },
|
||||
maxParallel: { bsonType: ["int", "null"] },
|
||||
approvalId: { bsonType: ["string", "null"] },
|
||||
gateMessage: { bsonType: ["string", "null"] },
|
||||
lastTransitionAt: { bsonType: ["date", "null"] },
|
||||
nextAttemptAt: { bsonType: ["date", "null"] },
|
||||
statusReason: { bsonType: ["string", "null"] }
|
||||
}
|
||||
}
|
||||
},
|
||||
tenantId: { bsonType: ["string", "null"] }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const logValidator = {
|
||||
$jsonSchema: {
|
||||
bsonType: "object",
|
||||
required: ["runId", "sequence", "timestamp", "level", "eventType", "message"],
|
||||
properties: {
|
||||
runId: { bsonType: "string" },
|
||||
sequence: { bsonType: "long" },
|
||||
timestamp: { bsonType: "date" },
|
||||
level: { bsonType: "string" },
|
||||
eventType: { bsonType: "string" },
|
||||
message: { bsonType: "string" },
|
||||
stepId: { bsonType: ["string", "null"] },
|
||||
metadata: { bsonType: ["object", "null"] }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const artifactsValidator = {
|
||||
$jsonSchema: {
|
||||
bsonType: "object",
|
||||
required: ["runId", "name", "type", "status", "capturedAt"],
|
||||
properties: {
|
||||
runId: { bsonType: "string" },
|
||||
name: { bsonType: "string" },
|
||||
type: { bsonType: "string" },
|
||||
status: { bsonType: "string" },
|
||||
capturedAt: { bsonType: "date" },
|
||||
sourcePath: { bsonType: ["string", "null"] },
|
||||
storedPath: { bsonType: ["string", "null"] },
|
||||
notes: { bsonType: ["string", "null"] },
|
||||
expression: { bsonType: ["object", "null"] }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const approvalsValidator = {
|
||||
$jsonSchema: {
|
||||
bsonType: "object",
|
||||
required: ["runId", "approvalId", "requestedAt", "status"],
|
||||
properties: {
|
||||
runId: { bsonType: "string" },
|
||||
approvalId: { bsonType: "string" },
|
||||
requiredGrants: { bsonType: "array", items: { bsonType: "string" } },
|
||||
stepIds: { bsonType: "array", items: { bsonType: "string" } },
|
||||
messages: { bsonType: "array", items: { bsonType: "string" } },
|
||||
reasonTemplate: { bsonType: ["string", "null"] },
|
||||
requestedAt: { bsonType: "date" },
|
||||
status: { bsonType: "string" },
|
||||
actorId: { bsonType: ["string", "null"] },
|
||||
completedAt: { bsonType: ["date", "null"] },
|
||||
summary: { bsonType: ["string", "null"] }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ensureCollection("pack_runs", runValidator);
|
||||
ensureCollection("pack_run_logs", logValidator);
|
||||
ensureCollection("pack_artifacts", artifactsValidator);
|
||||
ensureCollection("pack_run_approvals", approvalsValidator);
|
||||
|
||||
// Indexes for pack_runs
|
||||
db.pack_runs.createIndex({ updatedAt: -1 }, { name: "pack_runs_updatedAt_desc" });
|
||||
db.pack_runs.createIndex({ tenantId: 1, updatedAt: -1 }, { name: "pack_runs_tenant_updatedAt_desc", sparse: true });
|
||||
|
||||
// Indexes for pack_run_logs
|
||||
db.pack_run_logs.createIndex({ runId: 1, sequence: 1 }, { unique: true, name: "pack_run_logs_run_sequence" });
|
||||
db.pack_run_logs.createIndex({ runId: 1, timestamp: 1 }, { name: "pack_run_logs_run_timestamp" });
|
||||
|
||||
// Indexes for pack_artifacts
|
||||
db.pack_artifacts.createIndex({ runId: 1, name: 1 }, { unique: true, name: "pack_artifacts_run_name" });
|
||||
db.pack_artifacts.createIndex({ runId: 1 }, { name: "pack_artifacts_run" });
|
||||
|
||||
// Indexes for pack_run_approvals
|
||||
db.pack_run_approvals.createIndex({ runId: 1, approvalId: 1 }, { unique: true, name: "pack_run_approvals_run_approval" });
|
||||
db.pack_run_approvals.createIndex({ runId: 1, status: 1 }, { name: "pack_run_approvals_run_status" });
|
||||
@@ -0,0 +1,561 @@
|
||||
-- Partitioning Infrastructure Migration 001: Foundation
|
||||
-- Sprint: SPRINT_3422_0001_0001 - Time-Based Partitioning
|
||||
-- Category: C (infrastructure setup, requires planned maintenance)
|
||||
--
|
||||
-- Purpose: Create partition management infrastructure including:
|
||||
-- - Helper functions for partition creation and maintenance
|
||||
-- - Utility functions for BRIN index optimization
|
||||
-- - Partition maintenance scheduling support
|
||||
--
|
||||
-- This migration creates the foundation; table conversion is done in separate migrations.
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 1: Create partition management schema
|
||||
-- ============================================================================
|
||||
|
||||
CREATE SCHEMA IF NOT EXISTS partition_mgmt;
|
||||
|
||||
COMMENT ON SCHEMA partition_mgmt IS
|
||||
'Partition management utilities for time-series tables';
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 2: Managed table registration
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS partition_mgmt.managed_tables (
|
||||
schema_name TEXT NOT NULL,
|
||||
table_name TEXT NOT NULL,
|
||||
partition_key TEXT NOT NULL,
|
||||
partition_type TEXT NOT NULL,
|
||||
retention_months INT NOT NULL DEFAULT 0,
|
||||
months_ahead INT NOT NULL DEFAULT 3,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (schema_name, table_name)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE partition_mgmt.managed_tables IS
|
||||
'Tracks partitioned tables with retention and creation settings';
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 3: Partition creation function
|
||||
-- ============================================================================
|
||||
|
||||
-- Creates a new partition for a given table and date range
|
||||
CREATE OR REPLACE FUNCTION partition_mgmt.create_partition(
|
||||
p_schema_name TEXT,
|
||||
p_table_name TEXT,
|
||||
p_partition_column TEXT,
|
||||
p_start_date DATE,
|
||||
p_end_date DATE,
|
||||
p_partition_suffix TEXT DEFAULT NULL
|
||||
)
|
||||
RETURNS TEXT
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_partition_name TEXT;
|
||||
v_parent_table TEXT;
|
||||
v_sql TEXT;
|
||||
BEGIN
|
||||
v_parent_table := format('%I.%I', p_schema_name, p_table_name);
|
||||
|
||||
-- Generate partition name: tablename_YYYY_MM or tablename_YYYY_Q#
|
||||
IF p_partition_suffix IS NOT NULL THEN
|
||||
v_partition_name := format('%s_%s', p_table_name, p_partition_suffix);
|
||||
ELSE
|
||||
v_partition_name := format('%s_%s', p_table_name, to_char(p_start_date, 'YYYY_MM'));
|
||||
END IF;
|
||||
|
||||
-- Check if partition already exists
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
WHERE n.nspname = p_schema_name AND c.relname = v_partition_name
|
||||
) THEN
|
||||
RAISE NOTICE 'Partition % already exists, skipping', v_partition_name;
|
||||
RETURN v_partition_name;
|
||||
END IF;
|
||||
|
||||
-- Create partition
|
||||
v_sql := format(
|
||||
'CREATE TABLE %I.%I PARTITION OF %s FOR VALUES FROM (%L) TO (%L)',
|
||||
p_schema_name,
|
||||
v_partition_name,
|
||||
v_parent_table,
|
||||
p_start_date,
|
||||
p_end_date
|
||||
);
|
||||
|
||||
EXECUTE v_sql;
|
||||
|
||||
RAISE NOTICE 'Created partition %.%', p_schema_name, v_partition_name;
|
||||
RETURN v_partition_name;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 4: Monthly partition creation helper
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION partition_mgmt.create_monthly_partitions(
|
||||
p_schema_name TEXT,
|
||||
p_table_name TEXT,
|
||||
p_partition_column TEXT,
|
||||
p_start_month DATE,
|
||||
p_months_ahead INT DEFAULT 3
|
||||
)
|
||||
RETURNS SETOF TEXT
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_current_month DATE;
|
||||
v_end_month DATE;
|
||||
v_partition_name TEXT;
|
||||
BEGIN
|
||||
v_current_month := date_trunc('month', p_start_month)::DATE;
|
||||
v_end_month := date_trunc('month', NOW() + (p_months_ahead || ' months')::INTERVAL)::DATE;
|
||||
|
||||
WHILE v_current_month <= v_end_month LOOP
|
||||
v_partition_name := partition_mgmt.create_partition(
|
||||
p_schema_name,
|
||||
p_table_name,
|
||||
p_partition_column,
|
||||
v_current_month,
|
||||
(v_current_month + INTERVAL '1 month')::DATE
|
||||
);
|
||||
RETURN NEXT v_partition_name;
|
||||
v_current_month := (v_current_month + INTERVAL '1 month')::DATE;
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 5: Quarterly partition creation helper
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION partition_mgmt.create_quarterly_partitions(
|
||||
p_schema_name TEXT,
|
||||
p_table_name TEXT,
|
||||
p_partition_column TEXT,
|
||||
p_start_quarter DATE,
|
||||
p_quarters_ahead INT DEFAULT 2
|
||||
)
|
||||
RETURNS SETOF TEXT
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_current_quarter DATE;
|
||||
v_end_quarter DATE;
|
||||
v_partition_name TEXT;
|
||||
v_suffix TEXT;
|
||||
BEGIN
|
||||
v_current_quarter := date_trunc('quarter', p_start_quarter)::DATE;
|
||||
v_end_quarter := date_trunc('quarter', NOW() + (p_quarters_ahead * 3 || ' months')::INTERVAL)::DATE;
|
||||
|
||||
WHILE v_current_quarter <= v_end_quarter LOOP
|
||||
-- Generate suffix like 2025_Q1, 2025_Q2, etc.
|
||||
v_suffix := to_char(v_current_quarter, 'YYYY') || '_Q' ||
|
||||
EXTRACT(QUARTER FROM v_current_quarter)::TEXT;
|
||||
|
||||
v_partition_name := partition_mgmt.create_partition(
|
||||
p_schema_name,
|
||||
p_table_name,
|
||||
p_partition_column,
|
||||
v_current_quarter,
|
||||
(v_current_quarter + INTERVAL '3 months')::DATE,
|
||||
v_suffix
|
||||
);
|
||||
RETURN NEXT v_partition_name;
|
||||
v_current_quarter := (v_current_quarter + INTERVAL '3 months')::DATE;
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 6: Ensure future partitions exist
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION partition_mgmt.ensure_future_partitions(
|
||||
p_schema_name TEXT,
|
||||
p_table_name TEXT,
|
||||
p_months_ahead INT
|
||||
)
|
||||
RETURNS INT
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_partition_key TEXT;
|
||||
v_partition_type TEXT;
|
||||
v_months_ahead INT;
|
||||
v_created INT := 0;
|
||||
v_current DATE;
|
||||
v_end DATE;
|
||||
v_suffix TEXT;
|
||||
v_partition_name TEXT;
|
||||
BEGIN
|
||||
SELECT partition_key, partition_type, months_ahead
|
||||
INTO v_partition_key, v_partition_type, v_months_ahead
|
||||
FROM partition_mgmt.managed_tables
|
||||
WHERE schema_name = p_schema_name
|
||||
AND table_name = p_table_name;
|
||||
|
||||
IF v_partition_key IS NULL THEN
|
||||
RETURN 0;
|
||||
END IF;
|
||||
|
||||
IF p_months_ahead IS NOT NULL AND p_months_ahead > 0 THEN
|
||||
v_months_ahead := p_months_ahead;
|
||||
END IF;
|
||||
|
||||
IF v_months_ahead IS NULL OR v_months_ahead <= 0 THEN
|
||||
RETURN 0;
|
||||
END IF;
|
||||
|
||||
v_partition_type := lower(coalesce(v_partition_type, 'monthly'));
|
||||
|
||||
IF v_partition_type = 'monthly' THEN
|
||||
v_current := date_trunc('month', NOW())::DATE;
|
||||
v_end := date_trunc('month', NOW() + (v_months_ahead || ' months')::INTERVAL)::DATE;
|
||||
|
||||
WHILE v_current <= v_end LOOP
|
||||
v_partition_name := format('%s_%s', p_table_name, to_char(v_current, 'YYYY_MM'));
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
WHERE n.nspname = p_schema_name AND c.relname = v_partition_name
|
||||
) THEN
|
||||
PERFORM partition_mgmt.create_partition(
|
||||
p_schema_name,
|
||||
p_table_name,
|
||||
v_partition_key,
|
||||
v_current,
|
||||
(v_current + INTERVAL '1 month')::DATE
|
||||
);
|
||||
v_created := v_created + 1;
|
||||
END IF;
|
||||
|
||||
v_current := (v_current + INTERVAL '1 month')::DATE;
|
||||
END LOOP;
|
||||
ELSIF v_partition_type = 'quarterly' THEN
|
||||
v_current := date_trunc('quarter', NOW())::DATE;
|
||||
v_end := date_trunc('quarter', NOW() + (v_months_ahead || ' months')::INTERVAL)::DATE;
|
||||
|
||||
WHILE v_current <= v_end LOOP
|
||||
v_suffix := to_char(v_current, 'YYYY') || '_Q' ||
|
||||
EXTRACT(QUARTER FROM v_current)::TEXT;
|
||||
v_partition_name := format('%s_%s', p_table_name, v_suffix);
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
WHERE n.nspname = p_schema_name AND c.relname = v_partition_name
|
||||
) THEN
|
||||
PERFORM partition_mgmt.create_partition(
|
||||
p_schema_name,
|
||||
p_table_name,
|
||||
v_partition_key,
|
||||
v_current,
|
||||
(v_current + INTERVAL '3 months')::DATE,
|
||||
v_suffix
|
||||
);
|
||||
v_created := v_created + 1;
|
||||
END IF;
|
||||
|
||||
v_current := (v_current + INTERVAL '3 months')::DATE;
|
||||
END LOOP;
|
||||
END IF;
|
||||
|
||||
RETURN v_created;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 7: Retention enforcement function
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION partition_mgmt.enforce_retention(
|
||||
p_schema_name TEXT,
|
||||
p_table_name TEXT,
|
||||
p_retention_months INT
|
||||
)
|
||||
RETURNS INT
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_retention_months INT;
|
||||
v_cutoff_date DATE;
|
||||
v_partition RECORD;
|
||||
v_dropped INT := 0;
|
||||
BEGIN
|
||||
SELECT retention_months
|
||||
INTO v_retention_months
|
||||
FROM partition_mgmt.managed_tables
|
||||
WHERE schema_name = p_schema_name
|
||||
AND table_name = p_table_name;
|
||||
|
||||
IF p_retention_months IS NOT NULL AND p_retention_months > 0 THEN
|
||||
v_retention_months := p_retention_months;
|
||||
END IF;
|
||||
|
||||
IF v_retention_months IS NULL OR v_retention_months <= 0 THEN
|
||||
RETURN 0;
|
||||
END IF;
|
||||
|
||||
v_cutoff_date := (NOW() - (v_retention_months || ' months')::INTERVAL)::DATE;
|
||||
|
||||
FOR v_partition IN
|
||||
SELECT partition_name, partition_end
|
||||
FROM partition_mgmt.partition_stats
|
||||
WHERE schema_name = p_schema_name
|
||||
AND table_name = p_table_name
|
||||
LOOP
|
||||
IF v_partition.partition_end IS NOT NULL AND v_partition.partition_end < v_cutoff_date THEN
|
||||
EXECUTE format('DROP TABLE IF EXISTS %I.%I', p_schema_name, v_partition.partition_name);
|
||||
v_dropped := v_dropped + 1;
|
||||
END IF;
|
||||
END LOOP;
|
||||
|
||||
RETURN v_dropped;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 8: Partition detach and archive function
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION partition_mgmt.detach_partition(
|
||||
p_schema_name TEXT,
|
||||
p_table_name TEXT,
|
||||
p_partition_name TEXT,
|
||||
p_archive_schema TEXT DEFAULT 'archive'
|
||||
)
|
||||
RETURNS BOOLEAN
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_parent_table TEXT;
|
||||
v_partition_full TEXT;
|
||||
v_archive_table TEXT;
|
||||
BEGIN
|
||||
v_parent_table := format('%I.%I', p_schema_name, p_table_name);
|
||||
v_partition_full := format('%I.%I', p_schema_name, p_partition_name);
|
||||
v_archive_table := format('%I.%I', p_archive_schema, p_partition_name);
|
||||
|
||||
-- Create archive schema if not exists
|
||||
EXECUTE format('CREATE SCHEMA IF NOT EXISTS %I', p_archive_schema);
|
||||
|
||||
-- Detach partition
|
||||
EXECUTE format(
|
||||
'ALTER TABLE %s DETACH PARTITION %s',
|
||||
v_parent_table,
|
||||
v_partition_full
|
||||
);
|
||||
|
||||
-- Move to archive schema
|
||||
EXECUTE format(
|
||||
'ALTER TABLE %s SET SCHEMA %I',
|
||||
v_partition_full,
|
||||
p_archive_schema
|
||||
);
|
||||
|
||||
RAISE NOTICE 'Detached and archived partition % to %', p_partition_name, v_archive_table;
|
||||
RETURN TRUE;
|
||||
EXCEPTION
|
||||
WHEN OTHERS THEN
|
||||
RAISE WARNING 'Failed to detach partition %: %', p_partition_name, SQLERRM;
|
||||
RETURN FALSE;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 9: Partition retention cleanup function
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION partition_mgmt.cleanup_old_partitions(
|
||||
p_schema_name TEXT,
|
||||
p_table_name TEXT,
|
||||
p_retention_months INT,
|
||||
p_archive_schema TEXT DEFAULT 'archive',
|
||||
p_dry_run BOOLEAN DEFAULT TRUE
|
||||
)
|
||||
RETURNS TABLE(partition_name TEXT, action TEXT)
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_cutoff_date DATE;
|
||||
v_partition RECORD;
|
||||
v_partition_end DATE;
|
||||
BEGIN
|
||||
v_cutoff_date := (NOW() - (p_retention_months || ' months')::INTERVAL)::DATE;
|
||||
|
||||
FOR v_partition IN
|
||||
SELECT c.relname as name,
|
||||
pg_get_expr(c.relpartbound, c.oid) as bound_expr
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
JOIN pg_inherits i ON c.oid = i.inhrelid
|
||||
JOIN pg_class parent ON i.inhparent = parent.oid
|
||||
WHERE n.nspname = p_schema_name
|
||||
AND parent.relname = p_table_name
|
||||
AND c.relkind = 'r'
|
||||
LOOP
|
||||
-- Parse the partition bound to get end date
|
||||
-- Format: FOR VALUES FROM ('2024-01-01') TO ('2024-02-01')
|
||||
v_partition_end := (regexp_match(v_partition.bound_expr,
|
||||
'TO \(''([^'']+)''\)'))[1]::DATE;
|
||||
|
||||
IF v_partition_end IS NOT NULL AND v_partition_end < v_cutoff_date THEN
|
||||
partition_name := v_partition.name;
|
||||
|
||||
IF p_dry_run THEN
|
||||
action := 'WOULD_ARCHIVE';
|
||||
ELSE
|
||||
IF partition_mgmt.detach_partition(
|
||||
p_schema_name, p_table_name, v_partition.name, p_archive_schema
|
||||
) THEN
|
||||
action := 'ARCHIVED';
|
||||
ELSE
|
||||
action := 'FAILED';
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
RETURN NEXT;
|
||||
END IF;
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 10: Partition statistics view
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE VIEW partition_mgmt.partition_stats AS
|
||||
SELECT
|
||||
n.nspname AS schema_name,
|
||||
parent.relname AS table_name,
|
||||
c.relname AS partition_name,
|
||||
pg_get_expr(c.relpartbound, c.oid) AS partition_range,
|
||||
(regexp_match(pg_get_expr(c.relpartbound, c.oid), 'FROM \(''([^'']+)''\)'))[1]::DATE AS partition_start,
|
||||
(regexp_match(pg_get_expr(c.relpartbound, c.oid), 'TO \(''([^'']+)''\)'))[1]::DATE AS partition_end,
|
||||
pg_size_pretty(pg_relation_size(c.oid)) AS size,
|
||||
pg_relation_size(c.oid) AS size_bytes,
|
||||
COALESCE(s.n_live_tup, 0) AS estimated_rows,
|
||||
s.last_vacuum,
|
||||
s.last_autovacuum,
|
||||
s.last_analyze,
|
||||
s.last_autoanalyze
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
JOIN pg_inherits i ON c.oid = i.inhrelid
|
||||
JOIN pg_class parent ON i.inhparent = parent.oid
|
||||
LEFT JOIN pg_stat_user_tables s ON c.oid = s.relid
|
||||
WHERE c.relkind = 'r'
|
||||
AND parent.relkind = 'p'
|
||||
ORDER BY n.nspname, parent.relname, c.relname;
|
||||
|
||||
COMMENT ON VIEW partition_mgmt.partition_stats IS
|
||||
'Statistics for all partitioned tables in the database';
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 11: BRIN index optimization helper
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION partition_mgmt.create_brin_index_if_not_exists(
|
||||
p_schema_name TEXT,
|
||||
p_table_name TEXT,
|
||||
p_column_name TEXT,
|
||||
p_pages_per_range INT DEFAULT 128
|
||||
)
|
||||
RETURNS BOOLEAN
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_index_name TEXT;
|
||||
v_sql TEXT;
|
||||
BEGIN
|
||||
v_index_name := format('brin_%s_%s', p_table_name, p_column_name);
|
||||
|
||||
-- Check if index exists
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM pg_indexes
|
||||
WHERE schemaname = p_schema_name AND indexname = v_index_name
|
||||
) THEN
|
||||
RAISE NOTICE 'BRIN index % already exists', v_index_name;
|
||||
RETURN FALSE;
|
||||
END IF;
|
||||
|
||||
v_sql := format(
|
||||
'CREATE INDEX %I ON %I.%I USING brin (%I) WITH (pages_per_range = %s)',
|
||||
v_index_name,
|
||||
p_schema_name,
|
||||
p_table_name,
|
||||
p_column_name,
|
||||
p_pages_per_range
|
||||
);
|
||||
|
||||
EXECUTE v_sql;
|
||||
|
||||
RAISE NOTICE 'Created BRIN index % on %.%(%)',
|
||||
v_index_name, p_schema_name, p_table_name, p_column_name;
|
||||
RETURN TRUE;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 12: Maintenance job tracking table
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS partition_mgmt.maintenance_log (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
operation TEXT NOT NULL,
|
||||
schema_name TEXT NOT NULL,
|
||||
table_name TEXT NOT NULL,
|
||||
partition_name TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'started',
|
||||
details JSONB NOT NULL DEFAULT '{}',
|
||||
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
completed_at TIMESTAMPTZ,
|
||||
error_message TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX idx_maintenance_log_table ON partition_mgmt.maintenance_log(schema_name, table_name);
|
||||
CREATE INDEX idx_maintenance_log_status ON partition_mgmt.maintenance_log(status, started_at);
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 13: Archive schema for detached partitions
|
||||
-- ============================================================================
|
||||
|
||||
CREATE SCHEMA IF NOT EXISTS archive;
|
||||
|
||||
COMMENT ON SCHEMA archive IS
|
||||
'Storage for detached/archived partitions awaiting deletion or offload';
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- ============================================================================
|
||||
-- Usage Examples (commented out)
|
||||
-- ============================================================================
|
||||
|
||||
/*
|
||||
-- Create monthly partitions for audit table, 3 months ahead
|
||||
SELECT partition_mgmt.create_monthly_partitions(
|
||||
'scheduler', 'audit', 'created_at', '2024-01-01'::DATE, 3
|
||||
);
|
||||
|
||||
-- Preview old partitions that would be archived (dry run)
|
||||
SELECT * FROM partition_mgmt.cleanup_old_partitions(
|
||||
'scheduler', 'audit', 12, 'archive', TRUE
|
||||
);
|
||||
|
||||
-- Actually archive old partitions
|
||||
SELECT * FROM partition_mgmt.cleanup_old_partitions(
|
||||
'scheduler', 'audit', 12, 'archive', FALSE
|
||||
);
|
||||
|
||||
-- View partition statistics
|
||||
SELECT * FROM partition_mgmt.partition_stats
|
||||
WHERE schema_name = 'scheduler'
|
||||
ORDER BY table_name, partition_name;
|
||||
*/
|
||||
143
devops/database/postgres-partitioning/002_calibration_schema.sql
Normal file
143
devops/database/postgres-partitioning/002_calibration_schema.sql
Normal file
@@ -0,0 +1,143 @@
|
||||
-- Migration: Trust Vector Calibration Schema
|
||||
-- Sprint: 7100.0002.0002
|
||||
-- Description: Creates schema and tables for trust vector calibration system
|
||||
|
||||
-- Create calibration schema
|
||||
CREATE SCHEMA IF NOT EXISTS excititor_calibration;
|
||||
|
||||
-- Calibration manifests table
|
||||
-- Stores signed manifests for each calibration epoch
|
||||
CREATE TABLE IF NOT EXISTS excititor_calibration.calibration_manifests (
|
||||
manifest_id TEXT PRIMARY KEY,
|
||||
tenant_id TEXT NOT NULL,
|
||||
epoch_number INTEGER NOT NULL,
|
||||
epoch_start_utc TIMESTAMP NOT NULL,
|
||||
epoch_end_utc TIMESTAMP NOT NULL,
|
||||
sample_count INTEGER NOT NULL,
|
||||
learning_rate DOUBLE PRECISION NOT NULL,
|
||||
policy_hash TEXT,
|
||||
lattice_version TEXT NOT NULL,
|
||||
manifest_json JSONB NOT NULL,
|
||||
signature_envelope JSONB,
|
||||
created_at_utc TIMESTAMP NOT NULL DEFAULT (NOW() AT TIME ZONE 'UTC'),
|
||||
created_by TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT uq_calibration_manifest_tenant_epoch UNIQUE (tenant_id, epoch_number)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_calibration_manifests_tenant
|
||||
ON excititor_calibration.calibration_manifests(tenant_id);
|
||||
CREATE INDEX idx_calibration_manifests_created
|
||||
ON excititor_calibration.calibration_manifests(created_at_utc DESC);
|
||||
|
||||
-- Trust vector adjustments table
|
||||
-- Records each provider's trust vector changes per epoch
|
||||
CREATE TABLE IF NOT EXISTS excititor_calibration.trust_vector_adjustments (
|
||||
adjustment_id BIGSERIAL PRIMARY KEY,
|
||||
manifest_id TEXT NOT NULL REFERENCES excititor_calibration.calibration_manifests(manifest_id),
|
||||
source_id TEXT NOT NULL,
|
||||
old_provenance DOUBLE PRECISION NOT NULL,
|
||||
old_coverage DOUBLE PRECISION NOT NULL,
|
||||
old_replayability DOUBLE PRECISION NOT NULL,
|
||||
new_provenance DOUBLE PRECISION NOT NULL,
|
||||
new_coverage DOUBLE PRECISION NOT NULL,
|
||||
new_replayability DOUBLE PRECISION NOT NULL,
|
||||
adjustment_magnitude DOUBLE PRECISION NOT NULL,
|
||||
confidence_in_adjustment DOUBLE PRECISION NOT NULL,
|
||||
sample_count_for_source INTEGER NOT NULL,
|
||||
created_at_utc TIMESTAMP NOT NULL DEFAULT (NOW() AT TIME ZONE 'UTC'),
|
||||
|
||||
CONSTRAINT chk_old_provenance_range CHECK (old_provenance >= 0 AND old_provenance <= 1),
|
||||
CONSTRAINT chk_old_coverage_range CHECK (old_coverage >= 0 AND old_coverage <= 1),
|
||||
CONSTRAINT chk_old_replayability_range CHECK (old_replayability >= 0 AND old_replayability <= 1),
|
||||
CONSTRAINT chk_new_provenance_range CHECK (new_provenance >= 0 AND new_provenance <= 1),
|
||||
CONSTRAINT chk_new_coverage_range CHECK (new_coverage >= 0 AND new_coverage <= 1),
|
||||
CONSTRAINT chk_new_replayability_range CHECK (new_replayability >= 0 AND new_replayability <= 1),
|
||||
CONSTRAINT chk_confidence_range CHECK (confidence_in_adjustment >= 0 AND confidence_in_adjustment <= 1)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_trust_adjustments_manifest
|
||||
ON excititor_calibration.trust_vector_adjustments(manifest_id);
|
||||
CREATE INDEX idx_trust_adjustments_source
|
||||
ON excititor_calibration.trust_vector_adjustments(source_id);
|
||||
|
||||
-- Calibration feedback samples table
|
||||
-- Stores empirical evidence used for calibration
|
||||
CREATE TABLE IF NOT EXISTS excititor_calibration.calibration_samples (
|
||||
sample_id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id TEXT NOT NULL,
|
||||
source_id TEXT NOT NULL,
|
||||
cve_id TEXT NOT NULL,
|
||||
purl TEXT NOT NULL,
|
||||
expected_status TEXT NOT NULL,
|
||||
actual_status TEXT NOT NULL,
|
||||
verdict_confidence DOUBLE PRECISION NOT NULL,
|
||||
is_match BOOLEAN NOT NULL,
|
||||
feedback_source TEXT NOT NULL, -- 'reachability', 'customer_feedback', 'integration_tests'
|
||||
feedback_weight DOUBLE PRECISION NOT NULL DEFAULT 1.0,
|
||||
scan_id TEXT,
|
||||
collected_at_utc TIMESTAMP NOT NULL DEFAULT (NOW() AT TIME ZONE 'UTC'),
|
||||
processed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
processed_in_manifest_id TEXT REFERENCES excititor_calibration.calibration_manifests(manifest_id),
|
||||
|
||||
CONSTRAINT chk_verdict_confidence_range CHECK (verdict_confidence >= 0 AND verdict_confidence <= 1),
|
||||
CONSTRAINT chk_feedback_weight_range CHECK (feedback_weight >= 0 AND feedback_weight <= 1)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_calibration_samples_tenant
|
||||
ON excititor_calibration.calibration_samples(tenant_id);
|
||||
CREATE INDEX idx_calibration_samples_source
|
||||
ON excititor_calibration.calibration_samples(source_id);
|
||||
CREATE INDEX idx_calibration_samples_collected
|
||||
ON excititor_calibration.calibration_samples(collected_at_utc DESC);
|
||||
CREATE INDEX idx_calibration_samples_processed
|
||||
ON excititor_calibration.calibration_samples(processed) WHERE NOT processed;
|
||||
|
||||
-- Calibration metrics table
|
||||
-- Tracks performance metrics per source/severity/status
|
||||
CREATE TABLE IF NOT EXISTS excititor_calibration.calibration_metrics (
|
||||
metric_id BIGSERIAL PRIMARY KEY,
|
||||
manifest_id TEXT NOT NULL REFERENCES excititor_calibration.calibration_manifests(manifest_id),
|
||||
source_id TEXT,
|
||||
severity TEXT,
|
||||
status TEXT,
|
||||
precision DOUBLE PRECISION NOT NULL,
|
||||
recall DOUBLE PRECISION NOT NULL,
|
||||
f1_score DOUBLE PRECISION NOT NULL,
|
||||
false_positive_rate DOUBLE PRECISION NOT NULL,
|
||||
false_negative_rate DOUBLE PRECISION NOT NULL,
|
||||
sample_count INTEGER NOT NULL,
|
||||
created_at_utc TIMESTAMP NOT NULL DEFAULT (NOW() AT TIME ZONE 'UTC'),
|
||||
|
||||
CONSTRAINT chk_precision_range CHECK (precision >= 0 AND precision <= 1),
|
||||
CONSTRAINT chk_recall_range CHECK (recall >= 0 AND recall <= 1),
|
||||
CONSTRAINT chk_f1_range CHECK (f1_score >= 0 AND f1_score <= 1),
|
||||
CONSTRAINT chk_fpr_range CHECK (false_positive_rate >= 0 AND false_positive_rate <= 1),
|
||||
CONSTRAINT chk_fnr_range CHECK (false_negative_rate >= 0 AND false_negative_rate <= 1)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_calibration_metrics_manifest
|
||||
ON excititor_calibration.calibration_metrics(manifest_id);
|
||||
CREATE INDEX idx_calibration_metrics_source
|
||||
ON excititor_calibration.calibration_metrics(source_id) WHERE source_id IS NOT NULL;
|
||||
|
||||
-- Grant permissions to excititor service role
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'excititor_service') THEN
|
||||
GRANT USAGE ON SCHEMA excititor_calibration TO excititor_service;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA excititor_calibration TO excititor_service;
|
||||
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA excititor_calibration TO excititor_service;
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA excititor_calibration
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO excititor_service;
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA excititor_calibration
|
||||
GRANT USAGE, SELECT ON SEQUENCES TO excititor_service;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Comments for documentation
|
||||
COMMENT ON SCHEMA excititor_calibration IS 'Trust vector calibration data for VEX source scoring';
|
||||
COMMENT ON TABLE excititor_calibration.calibration_manifests IS 'Signed calibration epoch results';
|
||||
COMMENT ON TABLE excititor_calibration.trust_vector_adjustments IS 'Per-source trust vector changes per epoch';
|
||||
COMMENT ON TABLE excititor_calibration.calibration_samples IS 'Empirical feedback samples for calibration';
|
||||
COMMENT ON TABLE excititor_calibration.calibration_metrics IS 'Performance metrics per calibration epoch';
|
||||
@@ -0,0 +1,97 @@
|
||||
-- Provcache schema migration
|
||||
-- Run as: psql -d stellaops -f create_provcache_schema.sql
|
||||
|
||||
-- Create schema
|
||||
CREATE SCHEMA IF NOT EXISTS provcache;
|
||||
|
||||
-- Main cache items table
|
||||
CREATE TABLE IF NOT EXISTS provcache.provcache_items (
|
||||
verikey TEXT PRIMARY KEY,
|
||||
digest_version TEXT NOT NULL DEFAULT 'v1',
|
||||
verdict_hash TEXT NOT NULL,
|
||||
proof_root TEXT NOT NULL,
|
||||
replay_seed JSONB NOT NULL,
|
||||
policy_hash TEXT NOT NULL,
|
||||
signer_set_hash TEXT NOT NULL,
|
||||
feed_epoch TEXT NOT NULL,
|
||||
trust_score INTEGER NOT NULL CHECK (trust_score >= 0 AND trust_score <= 100),
|
||||
hit_count BIGINT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
last_accessed_at TIMESTAMPTZ,
|
||||
|
||||
-- Constraint: expires_at must be after created_at
|
||||
CONSTRAINT provcache_items_expires_check CHECK (expires_at > created_at)
|
||||
);
|
||||
|
||||
-- Indexes for invalidation queries
|
||||
CREATE INDEX IF NOT EXISTS idx_provcache_policy_hash
|
||||
ON provcache.provcache_items(policy_hash);
|
||||
CREATE INDEX IF NOT EXISTS idx_provcache_signer_set_hash
|
||||
ON provcache.provcache_items(signer_set_hash);
|
||||
CREATE INDEX IF NOT EXISTS idx_provcache_feed_epoch
|
||||
ON provcache.provcache_items(feed_epoch);
|
||||
CREATE INDEX IF NOT EXISTS idx_provcache_expires_at
|
||||
ON provcache.provcache_items(expires_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_provcache_created_at
|
||||
ON provcache.provcache_items(created_at);
|
||||
|
||||
-- Evidence chunks table for large evidence storage
|
||||
CREATE TABLE IF NOT EXISTS provcache.prov_evidence_chunks (
|
||||
chunk_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
proof_root TEXT NOT NULL,
|
||||
chunk_index INTEGER NOT NULL,
|
||||
chunk_hash TEXT NOT NULL,
|
||||
blob BYTEA NOT NULL,
|
||||
blob_size INTEGER NOT NULL,
|
||||
content_type TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
|
||||
CONSTRAINT prov_evidence_chunks_unique_index
|
||||
UNIQUE (proof_root, chunk_index)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_prov_chunks_proof_root
|
||||
ON provcache.prov_evidence_chunks(proof_root);
|
||||
|
||||
-- Revocation audit log
|
||||
CREATE TABLE IF NOT EXISTS provcache.prov_revocations (
|
||||
revocation_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
revocation_type TEXT NOT NULL,
|
||||
target_hash TEXT NOT NULL,
|
||||
reason TEXT,
|
||||
actor TEXT,
|
||||
entries_affected BIGINT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_prov_revocations_created_at
|
||||
ON provcache.prov_revocations(created_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_prov_revocations_target_hash
|
||||
ON provcache.prov_revocations(target_hash);
|
||||
|
||||
-- Function to update updated_at timestamp
|
||||
CREATE OR REPLACE FUNCTION provcache.update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
-- Trigger for auto-updating updated_at
|
||||
DROP TRIGGER IF EXISTS update_provcache_items_updated_at ON provcache.provcache_items;
|
||||
CREATE TRIGGER update_provcache_items_updated_at
|
||||
BEFORE UPDATE ON provcache.provcache_items
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION provcache.update_updated_at_column();
|
||||
|
||||
-- Grant permissions (adjust role as needed)
|
||||
-- GRANT USAGE ON SCHEMA provcache TO stellaops_app;
|
||||
-- GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA provcache TO stellaops_app;
|
||||
-- GRANT USAGE ON ALL SEQUENCES IN SCHEMA provcache TO stellaops_app;
|
||||
|
||||
COMMENT ON TABLE provcache.provcache_items IS 'Provenance cache entries for cached security decisions';
|
||||
COMMENT ON TABLE provcache.prov_evidence_chunks IS 'Chunked evidence storage for large SBOMs and attestations';
|
||||
COMMENT ON TABLE provcache.prov_revocations IS 'Audit log of cache invalidation events';
|
||||
159
devops/database/postgres-validation/001_validate_rls.sql
Normal file
159
devops/database/postgres-validation/001_validate_rls.sql
Normal file
@@ -0,0 +1,159 @@
|
||||
-- RLS Validation Script
|
||||
-- Sprint: SPRINT_3421_0001_0001 - RLS Expansion
|
||||
--
|
||||
-- Purpose: Verify that RLS is properly configured on all tenant-scoped tables
|
||||
-- Run this script after deploying RLS migrations to validate configuration
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 1: List all tables with RLS status
|
||||
-- ============================================================================
|
||||
|
||||
\echo '=== RLS Status for All Schemas ==='
|
||||
|
||||
SELECT
|
||||
schemaname AS schema,
|
||||
tablename AS table_name,
|
||||
rowsecurity AS rls_enabled,
|
||||
forcerowsecurity AS rls_forced,
|
||||
CASE
|
||||
WHEN rowsecurity AND forcerowsecurity THEN 'OK'
|
||||
WHEN rowsecurity AND NOT forcerowsecurity THEN 'WARN: Not forced'
|
||||
ELSE 'MISSING'
|
||||
END AS status
|
||||
FROM pg_tables
|
||||
WHERE schemaname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns')
|
||||
ORDER BY schemaname, tablename;
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 2: List all RLS policies
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== RLS Policies ==='
|
||||
|
||||
SELECT
|
||||
schemaname AS schema,
|
||||
tablename AS table_name,
|
||||
policyname AS policy_name,
|
||||
permissive,
|
||||
roles,
|
||||
cmd AS applies_to,
|
||||
qual IS NOT NULL AS has_using,
|
||||
with_check IS NOT NULL AS has_check
|
||||
FROM pg_policies
|
||||
WHERE schemaname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns')
|
||||
ORDER BY schemaname, tablename, policyname;
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 3: Tables missing RLS that should have it (have tenant_id column)
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== Tables with tenant_id but NO RLS ==='
|
||||
|
||||
SELECT
|
||||
c.table_schema AS schema,
|
||||
c.table_name AS table_name,
|
||||
'MISSING RLS' AS issue
|
||||
FROM information_schema.columns c
|
||||
JOIN pg_tables t ON c.table_schema = t.schemaname AND c.table_name = t.tablename
|
||||
WHERE c.column_name IN ('tenant_id', 'tenant')
|
||||
AND c.table_schema IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns')
|
||||
AND NOT t.rowsecurity
|
||||
ORDER BY c.table_schema, c.table_name;
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 4: Verify helper functions exist
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== RLS Helper Functions ==='
|
||||
|
||||
SELECT
|
||||
n.nspname AS schema,
|
||||
p.proname AS function_name,
|
||||
CASE
|
||||
WHEN p.prosecdef THEN 'SECURITY DEFINER'
|
||||
ELSE 'SECURITY INVOKER'
|
||||
END AS security,
|
||||
CASE
|
||||
WHEN p.provolatile = 's' THEN 'STABLE'
|
||||
WHEN p.provolatile = 'i' THEN 'IMMUTABLE'
|
||||
ELSE 'VOLATILE'
|
||||
END AS volatility
|
||||
FROM pg_proc p
|
||||
JOIN pg_namespace n ON p.pronamespace = n.oid
|
||||
WHERE p.proname = 'require_current_tenant'
|
||||
AND n.nspname LIKE '%_app'
|
||||
ORDER BY n.nspname;
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 5: Test RLS enforcement (expect failure without tenant context)
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== RLS Enforcement Test ==='
|
||||
\echo 'Testing RLS on scheduler.runs (should fail without tenant context)...'
|
||||
|
||||
-- Reset tenant context
|
||||
SELECT set_config('app.tenant_id', '', false);
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
-- This should raise an exception if RLS is working
|
||||
PERFORM * FROM scheduler.runs LIMIT 1;
|
||||
RAISE NOTICE 'WARNING: Query succeeded without tenant context - RLS may not be working!';
|
||||
EXCEPTION
|
||||
WHEN OTHERS THEN
|
||||
RAISE NOTICE 'OK: RLS blocked query without tenant context: %', SQLERRM;
|
||||
END
|
||||
$$;
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 6: Admin bypass role verification
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== Admin Bypass Roles ==='
|
||||
|
||||
SELECT
|
||||
rolname AS role_name,
|
||||
rolbypassrls AS can_bypass_rls,
|
||||
rolcanlogin AS can_login
|
||||
FROM pg_roles
|
||||
WHERE rolname LIKE '%_admin'
|
||||
AND rolbypassrls = TRUE
|
||||
ORDER BY rolname;
|
||||
|
||||
-- ============================================================================
|
||||
-- Summary
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== Summary ==='
|
||||
|
||||
SELECT
|
||||
'Total Tables' AS metric,
|
||||
COUNT(*)::TEXT AS value
|
||||
FROM pg_tables
|
||||
WHERE schemaname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns')
|
||||
UNION ALL
|
||||
SELECT
|
||||
'Tables with RLS Enabled',
|
||||
COUNT(*)::TEXT
|
||||
FROM pg_tables
|
||||
WHERE schemaname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns')
|
||||
AND rowsecurity = TRUE
|
||||
UNION ALL
|
||||
SELECT
|
||||
'Tables with RLS Forced',
|
||||
COUNT(*)::TEXT
|
||||
FROM pg_tables
|
||||
WHERE schemaname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns')
|
||||
AND forcerowsecurity = TRUE
|
||||
UNION ALL
|
||||
SELECT
|
||||
'Active Policies',
|
||||
COUNT(*)::TEXT
|
||||
FROM pg_policies
|
||||
WHERE schemaname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns');
|
||||
238
devops/database/postgres-validation/002_validate_partitions.sql
Normal file
238
devops/database/postgres-validation/002_validate_partitions.sql
Normal file
@@ -0,0 +1,238 @@
|
||||
-- Partition Validation Script
|
||||
-- Sprint: SPRINT_3422_0001_0001 - Time-Based Partitioning
|
||||
--
|
||||
-- Purpose: Verify that partitioned tables are properly configured and healthy
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 1: List all partitioned tables
|
||||
-- ============================================================================
|
||||
|
||||
\echo '=== Partitioned Tables ==='
|
||||
|
||||
SELECT
|
||||
n.nspname AS schema,
|
||||
c.relname AS table_name,
|
||||
CASE pt.partstrat
|
||||
WHEN 'r' THEN 'RANGE'
|
||||
WHEN 'l' THEN 'LIST'
|
||||
WHEN 'h' THEN 'HASH'
|
||||
END AS partition_strategy,
|
||||
array_to_string(array_agg(a.attname ORDER BY k.col), ', ') AS partition_key
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
JOIN pg_partitioned_table pt ON c.oid = pt.partrelid
|
||||
JOIN LATERAL unnest(pt.partattrs) WITH ORDINALITY AS k(col, idx) ON true
|
||||
LEFT JOIN pg_attribute a ON a.attrelid = c.oid AND a.attnum = k.col
|
||||
WHERE n.nspname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns', 'vuln')
|
||||
GROUP BY n.nspname, c.relname, pt.partstrat
|
||||
ORDER BY n.nspname, c.relname;
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 2: Partition inventory with sizes
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== Partition Inventory ==='
|
||||
|
||||
SELECT
|
||||
n.nspname AS schema,
|
||||
parent.relname AS parent_table,
|
||||
c.relname AS partition_name,
|
||||
pg_get_expr(c.relpartbound, c.oid) AS bounds,
|
||||
pg_size_pretty(pg_relation_size(c.oid)) AS size,
|
||||
s.n_live_tup AS estimated_rows
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
JOIN pg_inherits i ON c.oid = i.inhrelid
|
||||
JOIN pg_class parent ON i.inhparent = parent.oid
|
||||
LEFT JOIN pg_stat_user_tables s ON c.oid = s.relid
|
||||
WHERE n.nspname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns', 'vuln')
|
||||
AND c.relkind = 'r'
|
||||
AND parent.relkind = 'p'
|
||||
ORDER BY n.nspname, parent.relname, c.relname;
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 3: Check for missing future partitions
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== Future Partition Coverage ==='
|
||||
|
||||
WITH partition_bounds AS (
|
||||
SELECT
|
||||
n.nspname AS schema_name,
|
||||
parent.relname AS table_name,
|
||||
c.relname AS partition_name,
|
||||
-- Extract the TO date from partition bound
|
||||
(regexp_match(pg_get_expr(c.relpartbound, c.oid), 'TO \(''([^'']+)''\)'))[1]::DATE AS end_date
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
JOIN pg_inherits i ON c.oid = i.inhrelid
|
||||
JOIN pg_class parent ON i.inhparent = parent.oid
|
||||
WHERE c.relkind = 'r'
|
||||
AND parent.relkind = 'p'
|
||||
AND c.relname NOT LIKE '%_default'
|
||||
),
|
||||
max_bounds AS (
|
||||
SELECT
|
||||
schema_name,
|
||||
table_name,
|
||||
MAX(end_date) AS max_partition_date
|
||||
FROM partition_bounds
|
||||
WHERE end_date IS NOT NULL
|
||||
GROUP BY schema_name, table_name
|
||||
)
|
||||
SELECT
|
||||
schema_name,
|
||||
table_name,
|
||||
max_partition_date,
|
||||
(max_partition_date - CURRENT_DATE) AS days_ahead,
|
||||
CASE
|
||||
WHEN (max_partition_date - CURRENT_DATE) < 30 THEN 'CRITICAL: Create partitions!'
|
||||
WHEN (max_partition_date - CURRENT_DATE) < 60 THEN 'WARNING: Running low'
|
||||
ELSE 'OK'
|
||||
END AS status
|
||||
FROM max_bounds
|
||||
ORDER BY days_ahead;
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 4: Check for orphaned data in default partitions
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== Default Partition Data (should be empty) ==='
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
v_schema TEXT;
|
||||
v_table TEXT;
|
||||
v_count BIGINT;
|
||||
v_sql TEXT;
|
||||
BEGIN
|
||||
FOR v_schema, v_table IN
|
||||
SELECT n.nspname, c.relname
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
WHERE c.relname LIKE '%_default'
|
||||
AND n.nspname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns', 'vuln')
|
||||
LOOP
|
||||
v_sql := format('SELECT COUNT(*) FROM %I.%I', v_schema, v_table);
|
||||
EXECUTE v_sql INTO v_count;
|
||||
|
||||
IF v_count > 0 THEN
|
||||
RAISE NOTICE 'WARNING: %.% has % rows in default partition!',
|
||||
v_schema, v_table, v_count;
|
||||
ELSE
|
||||
RAISE NOTICE 'OK: %.% is empty', v_schema, v_table;
|
||||
END IF;
|
||||
END LOOP;
|
||||
END
|
||||
$$;
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 5: Index health on partitions
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== Partition Index Coverage ==='
|
||||
|
||||
SELECT
|
||||
schemaname AS schema,
|
||||
tablename AS table_name,
|
||||
indexname AS index_name,
|
||||
indexdef
|
||||
FROM pg_indexes
|
||||
WHERE schemaname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns', 'vuln')
|
||||
AND tablename LIKE '%_partitioned' OR tablename LIKE '%_202%'
|
||||
ORDER BY schemaname, tablename, indexname;
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 6: BRIN index effectiveness check
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== BRIN Index Statistics ==='
|
||||
|
||||
SELECT
|
||||
schemaname AS schema,
|
||||
tablename AS table_name,
|
||||
indexrelname AS index_name,
|
||||
idx_scan AS scans,
|
||||
idx_tup_read AS tuples_read,
|
||||
idx_tup_fetch AS tuples_fetched,
|
||||
pg_size_pretty(pg_relation_size(indexrelid)) AS index_size
|
||||
FROM pg_stat_user_indexes
|
||||
WHERE indexrelname LIKE 'brin_%'
|
||||
ORDER BY schemaname, tablename;
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 7: Partition maintenance recommendations
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== Maintenance Recommendations ==='
|
||||
|
||||
WITH partition_ages AS (
|
||||
SELECT
|
||||
n.nspname AS schema_name,
|
||||
parent.relname AS table_name,
|
||||
c.relname AS partition_name,
|
||||
(regexp_match(pg_get_expr(c.relpartbound, c.oid), 'FROM \(''([^'']+)''\)'))[1]::DATE AS start_date,
|
||||
(regexp_match(pg_get_expr(c.relpartbound, c.oid), 'TO \(''([^'']+)''\)'))[1]::DATE AS end_date
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
JOIN pg_inherits i ON c.oid = i.inhrelid
|
||||
JOIN pg_class parent ON i.inhparent = parent.oid
|
||||
WHERE c.relkind = 'r'
|
||||
AND parent.relkind = 'p'
|
||||
AND c.relname NOT LIKE '%_default'
|
||||
)
|
||||
SELECT
|
||||
schema_name,
|
||||
table_name,
|
||||
partition_name,
|
||||
start_date,
|
||||
end_date,
|
||||
(CURRENT_DATE - end_date) AS days_old,
|
||||
CASE
|
||||
WHEN (CURRENT_DATE - end_date) > 365 THEN 'Consider archiving (>1 year old)'
|
||||
WHEN (CURRENT_DATE - end_date) > 180 THEN 'Review retention policy (>6 months old)'
|
||||
ELSE 'Current'
|
||||
END AS recommendation
|
||||
FROM partition_ages
|
||||
WHERE start_date IS NOT NULL
|
||||
ORDER BY schema_name, table_name, start_date;
|
||||
|
||||
-- ============================================================================
|
||||
-- Summary
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== Summary ==='
|
||||
|
||||
SELECT
|
||||
'Partitioned Tables' AS metric,
|
||||
COUNT(DISTINCT parent.relname)::TEXT AS value
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
JOIN pg_inherits i ON c.oid = i.inhrelid
|
||||
JOIN pg_class parent ON i.inhparent = parent.oid
|
||||
WHERE n.nspname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns', 'vuln')
|
||||
AND parent.relkind = 'p'
|
||||
UNION ALL
|
||||
SELECT
|
||||
'Total Partitions',
|
||||
COUNT(*)::TEXT
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
JOIN pg_inherits i ON c.oid = i.inhrelid
|
||||
JOIN pg_class parent ON i.inhparent = parent.oid
|
||||
WHERE n.nspname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns', 'vuln')
|
||||
AND parent.relkind = 'p'
|
||||
UNION ALL
|
||||
SELECT
|
||||
'BRIN Indexes',
|
||||
COUNT(*)::TEXT
|
||||
FROM pg_indexes
|
||||
WHERE indexname LIKE 'brin_%'
|
||||
AND schemaname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns', 'vuln');
|
||||
66
devops/database/postgres/README.md
Normal file
66
devops/database/postgres/README.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# PostgreSQL 16 Cluster (staging / production)
|
||||
|
||||
This directory provisions StellaOps PostgreSQL clusters with **CloudNativePG (CNPG)**. It is pinned to Postgres 16.x, includes connection pooling (PgBouncer), Prometheus scraping, and S3-compatible backups. Everything is air-gap friendly: fetch the operator and images once, then render/apply manifests offline.
|
||||
|
||||
## Targets
|
||||
- **Staging:** `stellaops-pg-stg` (2 instances, 200 Gi data, WAL 64 Gi, PgBouncer x2)
|
||||
- **Production:** `stellaops-pg-prod` (3 instances, 500 Gi data, WAL 128 Gi, PgBouncer x3)
|
||||
- **Namespace:** `platform-postgres`
|
||||
|
||||
## Prerequisites
|
||||
- Kubernetes ≥ 1.27 with CSI storage classes `fast-ssd` (data) and `fast-wal` (WAL) available.
|
||||
- CloudNativePG operator 1.23.x mirrored or downloaded to `artifacts/cloudnative-pg-1.23.0.yaml`.
|
||||
- Images mirrored to your registry (example tags):
|
||||
- `ghcr.io/cloudnative-pg/postgresql:16.4`
|
||||
- `ghcr.io/cloudnative-pg/postgresql-operator:1.23.0`
|
||||
- `ghcr.io/cloudnative-pg/pgbouncer:1.23.0`
|
||||
- Secrets created from the templates under `ops/devops/postgres/secrets/` (superuser, app user, backup credentials).
|
||||
|
||||
## Render & Apply (deterministic)
|
||||
```bash
|
||||
# 1) Create namespace
|
||||
kubectl apply -f ops/devops/postgres/namespace.yaml
|
||||
|
||||
# 2) Install operator (offline-friendly: use the pinned manifest you mirrored)
|
||||
kubectl apply -f artifacts/cloudnative-pg-1.23.0.yaml
|
||||
|
||||
# 3) Create secrets (replace passwords/keys first)
|
||||
kubectl apply -f ops/devops/postgres/secrets/example-superuser.yaml
|
||||
kubectl apply -f ops/devops/postgres/secrets/example-app.yaml
|
||||
kubectl apply -f ops/devops/postgres/secrets/example-backup-credentials.yaml
|
||||
|
||||
# 4) Apply the cluster and pooler for the target environment
|
||||
kubectl apply -f ops/devops/postgres/cluster-staging.yaml
|
||||
kubectl apply -f ops/devops/postgres/pooler-staging.yaml
|
||||
# or
|
||||
kubectl apply -f ops/devops/postgres/cluster-production.yaml
|
||||
kubectl apply -f ops/devops/postgres/pooler-production.yaml
|
||||
```
|
||||
|
||||
## Connection Endpoints
|
||||
- RW service: `<cluster>-rw` (e.g., `stellaops-pg-stg-rw:5432`)
|
||||
- RO service: `<cluster>-ro`
|
||||
- PgBouncer pooler: `<pooler-name>` (e.g., `stellaops-pg-stg-pooler:6432`)
|
||||
|
||||
**Application connection string (matches library defaults):**
|
||||
`Host=stellaops-pg-stg-pooler;Port=6432;Username=stellaops_app;Password=<app-password>;Database=stellaops;Pooling=true;Timeout=15;CommandTimeout=30;Ssl Mode=Require;`
|
||||
|
||||
## Monitoring & Backups
|
||||
- `monitoring.enablePodMonitor: true` exposes PodMonitor for Prometheus Operator.
|
||||
- Barman/S3 backups are enabled by default; set `backup.barmanObjectStore.destinationPath` per env and populate `stellaops-pg-backup` credentials.
|
||||
- WAL compression is `gzip`; retention is operator-managed (configure via Barman bucket policies).
|
||||
|
||||
## Alignment with code defaults
|
||||
- Session settings: UTC timezone, 30s `statement_timeout`, tenant context via `set_config('app.current_tenant', ...)`.
|
||||
- Connection pooler uses **transaction** mode with a `server_reset_query` that clears session state, keeping RepositoryBase deterministic.
|
||||
|
||||
## Verification checklist
|
||||
- `kubectl get cluster -n platform-postgres` shows `Ready` replicas matching `instances`.
|
||||
- `kubectl logs deploy/cnpg-controller-manager -n cnpg-system` has no failing webhooks.
|
||||
- `kubectl get podmonitor -n platform-postgres` returns entries for the cluster and pooler.
|
||||
- `psql "<rw-connection-string>" -c 'select 1'` works from CI runner subnet.
|
||||
- `cnpg` `barman-cloud-backup-list` shows successful full + WAL backups.
|
||||
|
||||
## Offline notes
|
||||
- Mirror the operator manifest and container images to the approved registry first; no live downloads occur at runtime.
|
||||
- If Prometheus is not present, leave PodMonitor applied; it is inert without the CRD.
|
||||
57
devops/database/postgres/cluster-production.yaml
Normal file
57
devops/database/postgres/cluster-production.yaml
Normal file
@@ -0,0 +1,57 @@
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Cluster
|
||||
metadata:
|
||||
name: stellaops-pg-prod
|
||||
namespace: platform-postgres
|
||||
spec:
|
||||
instances: 3
|
||||
imageName: ghcr.io/cloudnative-pg/postgresql:16.4
|
||||
primaryUpdateStrategy: unsupervised
|
||||
storage:
|
||||
size: 500Gi
|
||||
storageClass: fast-ssd
|
||||
walStorage:
|
||||
size: 128Gi
|
||||
storageClass: fast-wal
|
||||
superuserSecret:
|
||||
name: stellaops-pg-superuser
|
||||
bootstrap:
|
||||
initdb:
|
||||
database: stellaops
|
||||
owner: stellaops_app
|
||||
secret:
|
||||
name: stellaops-pg-app
|
||||
monitoring:
|
||||
enablePodMonitor: true
|
||||
postgresql:
|
||||
parameters:
|
||||
max_connections: "900"
|
||||
shared_buffers: "4096MB"
|
||||
work_mem: "96MB"
|
||||
maintenance_work_mem: "768MB"
|
||||
wal_level: "replica"
|
||||
max_wal_size: "4GB"
|
||||
timezone: "UTC"
|
||||
log_min_duration_statement: "250"
|
||||
statement_timeout: "30000"
|
||||
resources:
|
||||
requests:
|
||||
cpu: "4"
|
||||
memory: "16Gi"
|
||||
limits:
|
||||
cpu: "8"
|
||||
memory: "24Gi"
|
||||
backup:
|
||||
barmanObjectStore:
|
||||
destinationPath: s3://stellaops-backups/production
|
||||
s3Credentials:
|
||||
accessKeyId:
|
||||
name: stellaops-pg-backup
|
||||
key: ACCESS_KEY_ID
|
||||
secretAccessKey:
|
||||
name: stellaops-pg-backup
|
||||
key: SECRET_ACCESS_KEY
|
||||
wal:
|
||||
compression: gzip
|
||||
maxParallel: 4
|
||||
logLevel: info
|
||||
57
devops/database/postgres/cluster-staging.yaml
Normal file
57
devops/database/postgres/cluster-staging.yaml
Normal file
@@ -0,0 +1,57 @@
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Cluster
|
||||
metadata:
|
||||
name: stellaops-pg-stg
|
||||
namespace: platform-postgres
|
||||
spec:
|
||||
instances: 2
|
||||
imageName: ghcr.io/cloudnative-pg/postgresql:16.4
|
||||
primaryUpdateStrategy: unsupervised
|
||||
storage:
|
||||
size: 200Gi
|
||||
storageClass: fast-ssd
|
||||
walStorage:
|
||||
size: 64Gi
|
||||
storageClass: fast-wal
|
||||
superuserSecret:
|
||||
name: stellaops-pg-superuser
|
||||
bootstrap:
|
||||
initdb:
|
||||
database: stellaops
|
||||
owner: stellaops_app
|
||||
secret:
|
||||
name: stellaops-pg-app
|
||||
monitoring:
|
||||
enablePodMonitor: true
|
||||
postgresql:
|
||||
parameters:
|
||||
max_connections: "600"
|
||||
shared_buffers: "2048MB"
|
||||
work_mem: "64MB"
|
||||
maintenance_work_mem: "512MB"
|
||||
wal_level: "replica"
|
||||
max_wal_size: "2GB"
|
||||
timezone: "UTC"
|
||||
log_min_duration_statement: "500"
|
||||
statement_timeout: "30000"
|
||||
resources:
|
||||
requests:
|
||||
cpu: "2"
|
||||
memory: "8Gi"
|
||||
limits:
|
||||
cpu: "4"
|
||||
memory: "12Gi"
|
||||
backup:
|
||||
barmanObjectStore:
|
||||
destinationPath: s3://stellaops-backups/staging
|
||||
s3Credentials:
|
||||
accessKeyId:
|
||||
name: stellaops-pg-backup
|
||||
key: ACCESS_KEY_ID
|
||||
secretAccessKey:
|
||||
name: stellaops-pg-backup
|
||||
key: SECRET_ACCESS_KEY
|
||||
wal:
|
||||
compression: gzip
|
||||
maxParallel: 2
|
||||
logLevel: info
|
||||
4
devops/database/postgres/namespace.yaml
Normal file
4
devops/database/postgres/namespace.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: platform-postgres
|
||||
29
devops/database/postgres/pooler-production.yaml
Normal file
29
devops/database/postgres/pooler-production.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Pooler
|
||||
metadata:
|
||||
name: stellaops-pg-prod-pooler
|
||||
namespace: platform-postgres
|
||||
spec:
|
||||
cluster:
|
||||
name: stellaops-pg-prod
|
||||
instances: 3
|
||||
type: rw
|
||||
pgbouncer:
|
||||
parameters:
|
||||
pool_mode: transaction
|
||||
max_client_conn: "1500"
|
||||
default_pool_size: "80"
|
||||
server_reset_query: "RESET ALL; SET SESSION AUTHORIZATION DEFAULT; SET TIME ZONE 'UTC';"
|
||||
authQuerySecret:
|
||||
name: stellaops-pg-app
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: pgbouncer
|
||||
resources:
|
||||
requests:
|
||||
cpu: "150m"
|
||||
memory: "192Mi"
|
||||
limits:
|
||||
cpu: "750m"
|
||||
memory: "384Mi"
|
||||
29
devops/database/postgres/pooler-staging.yaml
Normal file
29
devops/database/postgres/pooler-staging.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Pooler
|
||||
metadata:
|
||||
name: stellaops-pg-stg-pooler
|
||||
namespace: platform-postgres
|
||||
spec:
|
||||
cluster:
|
||||
name: stellaops-pg-stg
|
||||
instances: 2
|
||||
type: rw
|
||||
pgbouncer:
|
||||
parameters:
|
||||
pool_mode: transaction
|
||||
max_client_conn: "800"
|
||||
default_pool_size: "50"
|
||||
server_reset_query: "RESET ALL; SET SESSION AUTHORIZATION DEFAULT; SET TIME ZONE 'UTC';"
|
||||
authQuerySecret:
|
||||
name: stellaops-pg-app
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: pgbouncer
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "128Mi"
|
||||
limits:
|
||||
cpu: "500m"
|
||||
memory: "256Mi"
|
||||
9
devops/database/postgres/secrets/example-app.yaml
Normal file
9
devops/database/postgres/secrets/example-app.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: stellaops-pg-app
|
||||
namespace: platform-postgres
|
||||
type: kubernetes.io/basic-auth
|
||||
stringData:
|
||||
username: stellaops_app
|
||||
password: CHANGEME_APP_PASSWORD
|
||||
@@ -0,0 +1,9 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: stellaops-pg-backup
|
||||
namespace: platform-postgres
|
||||
type: Opaque
|
||||
stringData:
|
||||
ACCESS_KEY_ID: CHANGEME_ACCESS_KEY
|
||||
SECRET_ACCESS_KEY: CHANGEME_SECRET_KEY
|
||||
9
devops/database/postgres/secrets/example-superuser.yaml
Normal file
9
devops/database/postgres/secrets/example-superuser.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: stellaops-pg-superuser
|
||||
namespace: platform-postgres
|
||||
type: kubernetes.io/basic-auth
|
||||
stringData:
|
||||
username: postgres
|
||||
password: CHANGEME_SUPERUSER_PASSWORD
|
||||
15
devops/deployment/AGENTS.md
Normal file
15
devops/deployment/AGENTS.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Deployment & Operations — Agent Charter
|
||||
|
||||
## Mission
|
||||
Maintain deployment/upgrade/rollback workflows (Helm/Compose) per `docs/modules/devops/ARCHITECTURE.md` including environment-specific configs.
|
||||
|
||||
## Required Reading
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/modules/airgap/airgap-mode.md`
|
||||
|
||||
## Working Agreement
|
||||
- 1. Update task status to `DOING`/`DONE` inside the corresponding `docs/implplan/SPRINT_*.md` entry when you start or finish work.
|
||||
- 2. Review this charter and the Required Reading documents before coding; confirm prerequisites are met.
|
||||
- 3. Keep changes deterministic (stable ordering, timestamps, hashes) and align with offline/air-gap expectations.
|
||||
- 4. Coordinate doc updates, tests, and cross-guild communication whenever contracts or workflows change.
|
||||
- 5. Revert to `TODO` if you pause the task without shipping changes; leave notes in commit/PR descriptions for context.
|
||||
5
devops/deployment/TASKS.completed.md
Normal file
5
devops/deployment/TASKS.completed.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Completed Tasks
|
||||
|
||||
| ID | Status | Owner(s) | Depends on | Description | Exit Criteria |
|
||||
|----|--------|----------|------------|-------------|---------------|
|
||||
| DEVOPS-OPS-14-003 | DONE (2025-10-26) | Deployment Guild | DEVOPS-REL-14-001 | Document and script upgrade/rollback flows, channel management, and compatibility matrices per architecture. | Helm/Compose guides updated with digest pinning, automated checks committed, rollback drill recorded. |
|
||||
91
devops/deployment/advisory-ai/README.md
Normal file
91
devops/deployment/advisory-ai/README.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# Advisory AI Deployment Runbook
|
||||
|
||||
## Scope
|
||||
- Helm and Compose packaging for `advisory-ai-web` (API/plan cache) and `advisory-ai-worker` (inference/queue).
|
||||
- GPU toggle (NVIDIA) for on-prem inference; defaults remain CPU-safe.
|
||||
- Offline kit pickup instructions for including advisory AI artefacts.
|
||||
|
||||
## Helm
|
||||
Values already ship in `deploy/helm/stellaops/values-*.yaml` under `services.advisory-ai-web` and `advisory-ai-worker`.
|
||||
|
||||
GPU enablement (example):
|
||||
```yaml
|
||||
services:
|
||||
advisory-ai-worker:
|
||||
runtimeClassName: nvidia
|
||||
nodeSelector:
|
||||
nvidia.com/gpu.present: "true"
|
||||
tolerations:
|
||||
- key: nvidia.com/gpu
|
||||
operator: Exists
|
||||
effect: NoSchedule
|
||||
resources:
|
||||
limits:
|
||||
nvidia.com/gpu: 1
|
||||
advisory-ai-web:
|
||||
runtimeClassName: nvidia
|
||||
resources:
|
||||
limits:
|
||||
nvidia.com/gpu: 1
|
||||
```
|
||||
Apply:
|
||||
```bash
|
||||
helm upgrade --install stellaops ./deploy/helm/stellaops \
|
||||
-f deploy/helm/stellaops/values-prod.yaml \
|
||||
-f deploy/helm/stellaops/values-mirror.yaml \
|
||||
--set services.advisory-ai-worker.resources.limits.nvidia\.com/gpu=1 \
|
||||
--set services.advisory-ai-worker.runtimeClassName=nvidia
|
||||
```
|
||||
|
||||
## Compose
|
||||
- Base profiles: `docker-compose.dev.yaml`, `stage`, `prod`, `airgap` already include advisory AI services and shared volumes.
|
||||
- GPU overlay: `docker-compose.gpu.yaml` (adds NVIDIA device reservations and `ADVISORY_AI_INFERENCE_GPU=true`). Use:
|
||||
```bash
|
||||
docker compose --env-file prod.env \
|
||||
-f docker-compose.prod.yaml \
|
||||
-f docker-compose.gpu.yaml up -d
|
||||
```
|
||||
|
||||
## Offline kit pickup
|
||||
- Ensure advisory AI images are mirrored to your registry (or baked into airgap tar) before running the offline kit build.
|
||||
- Copy the following into `out/offline-kit/metadata/` before invoking the offline kit script:
|
||||
- `advisory-ai-web` image tar
|
||||
- `advisory-ai-worker` image tar
|
||||
- SBOM/provenance generated by the release pipeline
|
||||
- Verify `docs/24_OFFLINE_KIT.md` includes the advisory AI entries and rerun `tests/offline/test_build_offline_kit.py` if it changes.
|
||||
|
||||
## Runbook (prod quickstart)
|
||||
1) Prepare secrets in ExternalSecret or Kubernetes secret named `stellaops-prod-core` (see helm values).
|
||||
2) Run Helm install with prod values and GPU overrides as needed.
|
||||
3) For Compose, use `prod.env` and optionally `docker-compose.gpu.yaml` overlay.
|
||||
4) Validate health:
|
||||
- `GET /healthz` on `advisory-ai-web`
|
||||
- Check queue directories under `advisory-ai-*` volumes remain writable
|
||||
- Confirm inference path logs when GPU is detected (log key `advisory.ai.inference.gpu=true`).
|
||||
|
||||
## Advisory Feed Packaging (DEVOPS-AIAI-31-002)
|
||||
|
||||
Package advisory feeds (SBOM pointers + provenance) for release/offline kit:
|
||||
|
||||
```bash
|
||||
# Production (CI with COSIGN_PRIVATE_KEY_B64 secret)
|
||||
./ops/deployment/advisory-ai/package-advisory-feeds.sh
|
||||
|
||||
# Development (uses tools/cosign/cosign.dev.key)
|
||||
COSIGN_ALLOW_DEV_KEY=1 COSIGN_PASSWORD=stellaops-dev \
|
||||
./ops/deployment/advisory-ai/package-advisory-feeds.sh
|
||||
```
|
||||
|
||||
Outputs:
|
||||
- `out/advisory-ai/feeds/advisory-feeds.tar.gz` - Feed bundle
|
||||
- `out/advisory-ai/feeds/advisory-feeds.manifest.json` - Manifest with SBOM pointers
|
||||
- `out/advisory-ai/feeds/advisory-feeds.manifest.dsse.json` - DSSE signed manifest
|
||||
- `out/advisory-ai/feeds/provenance.json` - Build provenance
|
||||
|
||||
CI workflow: `.gitea/workflows/advisory-ai-release.yml`
|
||||
|
||||
## Evidence to attach (sprint)
|
||||
- Helm release output (rendered templates for advisory AI)
|
||||
- `docker-compose config` with/without GPU overlay
|
||||
- Offline kit metadata listing advisory AI images + SBOMs
|
||||
- Advisory feed package manifest with SBOM pointers
|
||||
165
devops/deployment/advisory-ai/package-advisory-feeds.sh
Normal file
165
devops/deployment/advisory-ai/package-advisory-feeds.sh
Normal file
@@ -0,0 +1,165 @@
|
||||
#!/usr/bin/env bash
|
||||
# Package advisory feeds (SBOM pointers + provenance) for release/offline kit
|
||||
# Usage: ./package-advisory-feeds.sh
|
||||
# Dev mode: COSIGN_ALLOW_DEV_KEY=1 COSIGN_PASSWORD=stellaops-dev ./package-advisory-feeds.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT=$(cd "$(dirname "$0")/../../.." && pwd)
|
||||
OUT_DIR="${OUT_DIR:-$ROOT/out/advisory-ai/feeds}"
|
||||
CREATED="${CREATED:-$(date -u +%Y-%m-%dT%H:%M:%SZ)}"
|
||||
|
||||
mkdir -p "$OUT_DIR"
|
||||
|
||||
# Key resolution (same pattern as tools/cosign/sign-signals.sh)
|
||||
resolve_key() {
|
||||
if [[ -n "${COSIGN_KEY_FILE:-}" && -f "$COSIGN_KEY_FILE" ]]; then
|
||||
echo "$COSIGN_KEY_FILE"
|
||||
elif [[ -n "${COSIGN_PRIVATE_KEY_B64:-}" ]]; then
|
||||
local tmp_key="$OUT_DIR/.cosign.key"
|
||||
echo "$COSIGN_PRIVATE_KEY_B64" | base64 -d > "$tmp_key"
|
||||
chmod 600 "$tmp_key"
|
||||
echo "$tmp_key"
|
||||
elif [[ -f "$ROOT/tools/cosign/cosign.key" ]]; then
|
||||
echo "$ROOT/tools/cosign/cosign.key"
|
||||
elif [[ "${COSIGN_ALLOW_DEV_KEY:-0}" == "1" && -f "$ROOT/tools/cosign/cosign.dev.key" ]]; then
|
||||
echo "[info] Using development key (non-production)" >&2
|
||||
echo "$ROOT/tools/cosign/cosign.dev.key"
|
||||
else
|
||||
echo "[error] No signing key available. Set COSIGN_PRIVATE_KEY_B64 or COSIGN_ALLOW_DEV_KEY=1" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
KEY_FILE=$(resolve_key)
|
||||
|
||||
# Collect advisory feed sources
|
||||
FEED_SOURCES=(
|
||||
"$ROOT/docs/samples/advisory-feeds"
|
||||
"$ROOT/src/AdvisoryAI/feeds"
|
||||
"$ROOT/out/feeds"
|
||||
)
|
||||
|
||||
echo "==> Collecting advisory feeds..."
|
||||
STAGE_DIR="$OUT_DIR/stage"
|
||||
mkdir -p "$STAGE_DIR"
|
||||
|
||||
for src in "${FEED_SOURCES[@]}"; do
|
||||
if [[ -d "$src" ]]; then
|
||||
echo " Adding feeds from $src"
|
||||
cp -r "$src"/* "$STAGE_DIR/" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
|
||||
# Create placeholder if no feeds found (dev mode)
|
||||
if [[ -z "$(ls -A "$STAGE_DIR" 2>/dev/null)" ]]; then
|
||||
echo "[info] No feed sources found; creating placeholder for dev mode"
|
||||
cat > "$STAGE_DIR/placeholder.json" <<EOF
|
||||
{
|
||||
"type": "advisory-feed-placeholder",
|
||||
"created": "$CREATED",
|
||||
"note": "Placeholder for development; replace with real feeds in production"
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Create feed bundle
|
||||
echo "==> Creating feed bundle..."
|
||||
BUNDLE_TAR="$OUT_DIR/advisory-feeds.tar.gz"
|
||||
tar -czf "$BUNDLE_TAR" -C "$STAGE_DIR" .
|
||||
|
||||
# Compute hashes
|
||||
sha256() {
|
||||
sha256sum "$1" | awk '{print $1}'
|
||||
}
|
||||
|
||||
BUNDLE_HASH=$(sha256 "$BUNDLE_TAR")
|
||||
|
||||
# Generate manifest with SBOM pointers
|
||||
echo "==> Generating manifest..."
|
||||
MANIFEST="$OUT_DIR/advisory-feeds.manifest.json"
|
||||
cat > "$MANIFEST" <<EOF
|
||||
{
|
||||
"schemaVersion": "1.0.0",
|
||||
"created": "$CREATED",
|
||||
"bundle": {
|
||||
"path": "advisory-feeds.tar.gz",
|
||||
"sha256": "$BUNDLE_HASH",
|
||||
"size": $(stat -c%s "$BUNDLE_TAR" 2>/dev/null || stat -f%z "$BUNDLE_TAR")
|
||||
},
|
||||
"sbom": {
|
||||
"format": "spdx-json",
|
||||
"path": "advisory-feeds.sbom.json",
|
||||
"note": "SBOM generated during CI; pointer only in manifest"
|
||||
},
|
||||
"provenance": {
|
||||
"path": "provenance.json",
|
||||
"builder": "stellaops-advisory-ai-release"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Sign manifest with DSSE
|
||||
echo "==> Signing manifest..."
|
||||
DSSE_OUT="$OUT_DIR/advisory-feeds.manifest.dsse.json"
|
||||
|
||||
# Check for cosign
|
||||
COSIGN="${COSIGN:-$ROOT/tools/cosign/cosign}"
|
||||
if ! command -v cosign &>/dev/null && [[ ! -x "$COSIGN" ]]; then
|
||||
echo "[warn] cosign not found; skipping DSSE signing" >&2
|
||||
else
|
||||
COSIGN_CMD="${COSIGN:-cosign}"
|
||||
if command -v cosign &>/dev/null; then
|
||||
COSIGN_CMD="cosign"
|
||||
fi
|
||||
|
||||
COSIGN_PASSWORD="${COSIGN_PASSWORD:-}" "$COSIGN_CMD" sign-blob \
|
||||
--key "$KEY_FILE" \
|
||||
--bundle "$DSSE_OUT" \
|
||||
--tlog-upload=false \
|
||||
--yes \
|
||||
"$MANIFEST" 2>/dev/null || echo "[warn] DSSE signing skipped (cosign error)"
|
||||
fi
|
||||
|
||||
# Generate provenance
|
||||
echo "==> Generating provenance..."
|
||||
PROVENANCE="$OUT_DIR/provenance.json"
|
||||
cat > "$PROVENANCE" <<EOF
|
||||
{
|
||||
"_type": "https://in-toto.io/Statement/v1",
|
||||
"subject": [
|
||||
{
|
||||
"name": "advisory-feeds.tar.gz",
|
||||
"digest": {"sha256": "$BUNDLE_HASH"}
|
||||
}
|
||||
],
|
||||
"predicateType": "https://slsa.dev/provenance/v1",
|
||||
"predicate": {
|
||||
"buildDefinition": {
|
||||
"buildType": "https://stella-ops.org/advisory-ai-release/v1",
|
||||
"externalParameters": {},
|
||||
"internalParameters": {
|
||||
"created": "$CREATED"
|
||||
}
|
||||
},
|
||||
"runDetails": {
|
||||
"builder": {
|
||||
"id": "https://stella-ops.org/advisory-ai-release"
|
||||
},
|
||||
"metadata": {
|
||||
"invocationId": "$(uuidgen 2>/dev/null || echo "dev-$(date +%s)")",
|
||||
"startedOn": "$CREATED"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Cleanup temp key
|
||||
[[ -f "$OUT_DIR/.cosign.key" ]] && rm -f "$OUT_DIR/.cosign.key"
|
||||
|
||||
echo "==> Advisory feed packaging complete"
|
||||
echo " Bundle: $BUNDLE_TAR"
|
||||
echo " Manifest: $MANIFEST"
|
||||
echo " DSSE: $DSSE_OUT"
|
||||
echo " Provenance: $PROVENANCE"
|
||||
107
devops/deployment/cli/README.md
Normal file
107
devops/deployment/cli/README.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# StellaOps CLI Release Packaging
|
||||
|
||||
## Scope
|
||||
- Package and publish StellaOps CLI binaries for all supported OS/arch targets with checksums, signatures, completions, and a container image.
|
||||
- Outputs feed three lanes: (1) public release mirrors, (2) air-gapped/offline kit, (3) internal regression runners.
|
||||
- Source artefacts come from DevOps pipelines (`.gitea/workflows/cli-build.yml`, `.gitea/workflows/cli-chaos-parity.yml`).
|
||||
|
||||
## Inputs (expected layout)
|
||||
```
|
||||
out/cli/<version>/
|
||||
stella-cli-linux-amd64.tar.gz
|
||||
stella-cli-linux-arm64.tar.gz
|
||||
stella-cli-darwin-arm64.tar.gz
|
||||
stella-cli-windows-amd64.zip
|
||||
completions/
|
||||
bash/stella
|
||||
zsh/_stella
|
||||
fish/stella.fish
|
||||
parity/
|
||||
parity-report.json
|
||||
sbom/
|
||||
stella-cli.spdx.json
|
||||
```
|
||||
`<version>` must match the git tag and container tag (e.g., `2025.12.0`).
|
||||
|
||||
## Packaging steps (deterministic)
|
||||
1) Set version and workdir
|
||||
```bash
|
||||
export CLI_VERSION=2025.12.0
|
||||
export CLI_OUT=out/cli/$CLI_VERSION
|
||||
```
|
||||
|
||||
2) Generate checksums (sorted, LF endings)
|
||||
```bash
|
||||
cd "$CLI_OUT"
|
||||
find . -maxdepth 1 -type f \( -name 'stella-cli-*' -o -name '*.zip' \) \
|
||||
-print0 | sort -z | xargs -0 sha256sum > SHA256SUMS
|
||||
```
|
||||
|
||||
3) Sign checksum file (cosign keyless or key)
|
||||
```bash
|
||||
COSIGN_YES=true cosign sign-blob \
|
||||
--key env://MIRROR_SIGN_KEY_B64 \
|
||||
--output-signature SHA256SUMS.sig \
|
||||
--output-certificate SHA256SUMS.pem \
|
||||
SHA256SUMS
|
||||
```
|
||||
|
||||
4) Build/push container image (optional if pipeline already produced)
|
||||
```bash
|
||||
docker build -t registry.local/stella/cli:$CLI_VERSION -f deploy/compose/cli/Dockerfile .
|
||||
docker push registry.local/stella/cli:$CLI_VERSION
|
||||
```
|
||||
|
||||
5) Produce offline image tar (for airgap kit)
|
||||
```bash
|
||||
docker pull registry.local/stella/cli:$CLI_VERSION
|
||||
docker save registry.local/stella/cli:$CLI_VERSION \
|
||||
| gzip -9 > stella-cli-image-$CLI_VERSION.tar.gz
|
||||
```
|
||||
|
||||
6) Bundle completions
|
||||
```bash
|
||||
tar -C "$CLI_OUT/completions" -czf stella-cli-completions-$CLI_VERSION.tar.gz .
|
||||
```
|
||||
|
||||
7) Publish artefact manifest (for mirrors/offline kit)
|
||||
```bash
|
||||
cat > release-manifest-$CLI_VERSION.json <<'EOF'
|
||||
{
|
||||
"version": "REPLACE_VERSION",
|
||||
"binaries": [
|
||||
"stella-cli-linux-amd64.tar.gz",
|
||||
"stella-cli-linux-arm64.tar.gz",
|
||||
"stella-cli-darwin-arm64.tar.gz",
|
||||
"stella-cli-windows-amd64.zip"
|
||||
],
|
||||
"completions": "stella-cli-completions-REPLACE_VERSION.tar.gz",
|
||||
"checksums": "SHA256SUMS",
|
||||
"signatures": ["SHA256SUMS.sig", "SHA256SUMS.pem"],
|
||||
"container": {
|
||||
"image": "registry.local/stella/cli:REPLACE_VERSION",
|
||||
"offline_tar": "stella-cli-image-REPLACE_VERSION.tar.gz"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
sed -i "s/REPLACE_VERSION/$CLI_VERSION/g" release-manifest-$CLI_VERSION.json
|
||||
```
|
||||
|
||||
## Distribution lanes
|
||||
- **Mirror / public:** upload binaries, completions, SBOM, `SHA256SUMS*`, and `release-manifest-<version>.json` to the mirror bucket; expose via CDN.
|
||||
- **Offline kit:** copy the same files plus `stella-cli-image-<version>.tar.gz` into `out/offline-kit/cli/` before running `ops/offline-kit/scripts/build_offline_kit.sh`.
|
||||
- **Internal runners:** sync `SHA256SUMS` and `SHA256SUMS.sig` to the runner cache; store container tar in the runner image cache path.
|
||||
|
||||
## Verification
|
||||
```bash
|
||||
cd "$CLI_OUT"
|
||||
sha256sum --check SHA256SUMS
|
||||
cosign verify-blob --key env://MIRROR_SIGN_KEY_B64 --signature SHA256SUMS.sig --certificate SHA256SUMS.pem SHA256SUMS
|
||||
```
|
||||
|
||||
## Rollback / re-spin
|
||||
- To revoke a bad drop, delete the mirror path for that version and reissue `release-manifest-<version>.json` with `"revoked": true` field; keep signatures for audit.
|
||||
- Re-spin by rerunning steps with a new version tag; never overwrite artefacts in-place.
|
||||
|
||||
## Evidence to attach in sprint
|
||||
- `SHA256SUMS`, `SHA256SUMS.sig`, `release-manifest-<version>.json`, and offline image tar path uploaded to sprint evidence locker.
|
||||
35
devops/deployment/export/helm-overlays.md
Normal file
35
devops/deployment/export/helm-overlays.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Export Center Helm Overlays (DEPLOY-EXPORT-35-001)
|
||||
|
||||
## Values files (download-only)
|
||||
- `deploy/helm/stellaops/values-export.yaml` (add) with:
|
||||
- `exportcenter:`
|
||||
- `image.repository`: `registry.stella-ops.org/export-center`
|
||||
- `image.tag`: set via pipeline
|
||||
- `objectStorage.endpoint`: `http://minio:9000`
|
||||
- `objectStorage.bucket`: `export-prod`
|
||||
- `objectStorage.accessKeySecret`: `exportcenter-minio`
|
||||
- `objectStorage.secretKeySecret`: `exportcenter-minio`
|
||||
- `signing.kmsKey`: `exportcenter-kms`
|
||||
- `signing.kmsRegion`: `us-east-1`
|
||||
- `dsse.enabled`: true
|
||||
|
||||
## Secrets
|
||||
- KMS signing: create secret `exportcenter-kms` with JSON key material (KMS provider specific). Example: `ops/deployment/export/secrets-example.yaml`.
|
||||
- MinIO creds: `exportcenter-minio` with `accesskey`, `secretkey` keys (see example manifest).
|
||||
|
||||
## Rollout
|
||||
- `helm upgrade --install export-center deploy/helm/stellaops -f deploy/helm/stellaops/values-export.yaml --set image.tag=$TAG`
|
||||
- Pre-flight: `helm template ...` and `helm lint`.
|
||||
- Post: verify readiness `kubectl rollout status deploy/export-center` and run `curl /healthz`.
|
||||
|
||||
## Rollback
|
||||
- `helm rollback export-center <rev>`; ensure previous tag exists.
|
||||
|
||||
## Required artefacts
|
||||
- Signed images + provenance (from release pipeline).
|
||||
- SBOM attached via registry (cosign attestations acceptable).
|
||||
|
||||
## Acceptance
|
||||
- Overlay renders without missing values.
|
||||
- Secrets documented and referenced in template.
|
||||
- Rollout/rollback steps documented.
|
||||
15
devops/deployment/export/secrets-example.yaml
Normal file
15
devops/deployment/export/secrets-example.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: exportcenter-minio
|
||||
stringData:
|
||||
accesskey: REPLACE_ME
|
||||
secretkey: REPLACE_ME
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: exportcenter-kms
|
||||
stringData:
|
||||
key.json: |
|
||||
{"kmsProvider":"awskms","keyId":"arn:aws:kms:...","region":"us-east-1"}
|
||||
28
devops/deployment/notify/helm-overlays.md
Normal file
28
devops/deployment/notify/helm-overlays.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Notifier Helm Overlays (DEPLOY-NOTIFY-38-001)
|
||||
|
||||
## Values file
|
||||
- `deploy/helm/stellaops/values-notify.yaml` (added) with:
|
||||
- `notify:`
|
||||
- `image.repository`: `registry.stella-ops.org/notify`
|
||||
- `image.tag`: set by pipeline
|
||||
- `smtp.host`, `smtp.port`, `smtp.usernameSecret`, `smtp.passwordSecret`
|
||||
- `webhook.allowedHosts`: list
|
||||
- `chat.webhookSecret`: secret name for chat tokens
|
||||
- `tls.secretName`: optional ingress cert
|
||||
|
||||
## Secrets
|
||||
- SMTP creds secret `notify-smtp` with keys `username`, `password` (see `ops/deployment/notify/secrets-example.yaml`).
|
||||
- Chat/webhook secret `notify-chat` with key `token` (see example manifest).
|
||||
|
||||
## Rollout
|
||||
- `helm upgrade --install notify deploy/helm/stellaops -f deploy/helm/stellaops/values-notify.yaml --set image.tag=$TAG`
|
||||
- Pre-flight: `helm lint`, `helm template`.
|
||||
- Post: `kubectl rollout status deploy/notify` and `curl /healthz`.
|
||||
|
||||
## Rollback
|
||||
- `helm rollback notify <rev>`; confirm previous image tag exists.
|
||||
|
||||
## Acceptance
|
||||
- Overlay renders without missing values.
|
||||
- Secrets documented and referenced.
|
||||
- Rollout/rollback steps documented.
|
||||
14
devops/deployment/notify/secrets-example.yaml
Normal file
14
devops/deployment/notify/secrets-example.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: notify-smtp
|
||||
stringData:
|
||||
username: REPLACE_ME
|
||||
password: REPLACE_ME
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: notify-chat
|
||||
stringData:
|
||||
token: REPLACE_ME
|
||||
150
devops/docker/Dockerfile.ci
Normal file
150
devops/docker/Dockerfile.ci
Normal file
@@ -0,0 +1,150 @@
|
||||
# Dockerfile.ci - Local CI testing container matching Gitea runner environment
|
||||
# Sprint: SPRINT_20251226_006_CICD
|
||||
#
|
||||
# Usage:
|
||||
# docker build -t stellaops-ci:local -f devops/docker/Dockerfile.ci .
|
||||
# docker run --rm -v $(pwd):/src stellaops-ci:local ./devops/scripts/test-local.sh
|
||||
|
||||
FROM ubuntu:22.04
|
||||
|
||||
LABEL org.opencontainers.image.title="StellaOps CI"
|
||||
LABEL org.opencontainers.image.description="Local CI testing environment matching Gitea runner"
|
||||
LABEL org.opencontainers.image.source="https://git.stella-ops.org/stella-ops.org/git.stella-ops.org"
|
||||
|
||||
# Environment variables
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV DOTNET_VERSION=10.0.100
|
||||
ENV NODE_VERSION=20
|
||||
ENV HELM_VERSION=3.16.0
|
||||
ENV COSIGN_VERSION=2.2.4
|
||||
ENV TZ=UTC
|
||||
|
||||
# Disable .NET telemetry
|
||||
ENV DOTNET_NOLOGO=1
|
||||
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
|
||||
|
||||
# .NET paths
|
||||
ENV DOTNET_ROOT=/usr/share/dotnet
|
||||
ENV PATH="/usr/share/dotnet:/root/.dotnet/tools:${PATH}"
|
||||
|
||||
# ===========================================================================
|
||||
# BASE DEPENDENCIES
|
||||
# ===========================================================================
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
# Core utilities
|
||||
curl \
|
||||
wget \
|
||||
gnupg2 \
|
||||
ca-certificates \
|
||||
git \
|
||||
unzip \
|
||||
jq \
|
||||
# Build tools
|
||||
build-essential \
|
||||
# Docker CLI (for DinD scenarios)
|
||||
docker.io \
|
||||
docker-compose-plugin \
|
||||
# Cross-compilation
|
||||
binutils-aarch64-linux-gnu \
|
||||
# Python (for scripts)
|
||||
python3 \
|
||||
python3-pip \
|
||||
# Locales
|
||||
locales \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Set locale
|
||||
RUN locale-gen en_US.UTF-8
|
||||
ENV LANG=en_US.UTF-8
|
||||
ENV LANGUAGE=en_US:en
|
||||
ENV LC_ALL=en_US.UTF-8
|
||||
|
||||
# ===========================================================================
|
||||
# POSTGRESQL CLIENT 16
|
||||
# ===========================================================================
|
||||
|
||||
RUN curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor -o /usr/share/keyrings/postgresql-archive-keyring.gpg \
|
||||
&& echo "deb [signed-by=/usr/share/keyrings/postgresql-archive-keyring.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y --no-install-recommends postgresql-client-16 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# ===========================================================================
|
||||
# .NET 10 SDK
|
||||
# ===========================================================================
|
||||
|
||||
RUN curl -fsSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh \
|
||||
&& chmod +x /tmp/dotnet-install.sh \
|
||||
&& /tmp/dotnet-install.sh --version ${DOTNET_VERSION} --install-dir /usr/share/dotnet \
|
||||
&& rm /tmp/dotnet-install.sh \
|
||||
&& dotnet --version
|
||||
|
||||
# Install common .NET tools
|
||||
RUN dotnet tool install -g trx2junit \
|
||||
&& dotnet tool install -g dotnet-reportgenerator-globaltool
|
||||
|
||||
# ===========================================================================
|
||||
# NODE.JS 20
|
||||
# ===========================================================================
|
||||
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
|
||||
&& apt-get install -y --no-install-recommends nodejs \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& node --version \
|
||||
&& npm --version
|
||||
|
||||
# ===========================================================================
|
||||
# HELM 3.16.0
|
||||
# ===========================================================================
|
||||
|
||||
RUN curl -fsSL https://get.helm.sh/helm-v${HELM_VERSION}-linux-amd64.tar.gz | \
|
||||
tar -xzf - -C /tmp \
|
||||
&& mv /tmp/linux-amd64/helm /usr/local/bin/helm \
|
||||
&& rm -rf /tmp/linux-amd64 \
|
||||
&& helm version
|
||||
|
||||
# ===========================================================================
|
||||
# COSIGN
|
||||
# ===========================================================================
|
||||
|
||||
RUN curl -fsSL https://github.com/sigstore/cosign/releases/download/v${COSIGN_VERSION}/cosign-linux-amd64 \
|
||||
-o /usr/local/bin/cosign \
|
||||
&& chmod +x /usr/local/bin/cosign \
|
||||
&& cosign version
|
||||
|
||||
# ===========================================================================
|
||||
# SYFT (SBOM generation)
|
||||
# ===========================================================================
|
||||
|
||||
RUN curl -fsSL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
|
||||
|
||||
# ===========================================================================
|
||||
# SETUP
|
||||
# ===========================================================================
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
# Create non-root user for safer execution (optional)
|
||||
RUN useradd -m -s /bin/bash ciuser \
|
||||
&& mkdir -p /home/ciuser/.dotnet/tools \
|
||||
&& chown -R ciuser:ciuser /home/ciuser
|
||||
|
||||
# Health check script
|
||||
COPY --chmod=755 <<'EOF' /usr/local/bin/ci-health-check
|
||||
#!/bin/bash
|
||||
set -e
|
||||
echo "=== CI Environment Health Check ==="
|
||||
echo "OS: $(cat /etc/os-release | grep PRETTY_NAME | cut -d= -f2)"
|
||||
echo ".NET: $(dotnet --version)"
|
||||
echo "Node: $(node --version)"
|
||||
echo "npm: $(npm --version)"
|
||||
echo "Helm: $(helm version --short)"
|
||||
echo "Cosign: $(cosign version 2>&1 | head -1)"
|
||||
echo "Docker: $(docker --version 2>/dev/null || echo 'Not available')"
|
||||
echo "PostgreSQL client: $(psql --version)"
|
||||
echo "=== All checks passed ==="
|
||||
EOF
|
||||
|
||||
ENTRYPOINT ["/bin/bash"]
|
||||
40
devops/docker/Dockerfile.console
Normal file
40
devops/docker/Dockerfile.console
Normal file
@@ -0,0 +1,40 @@
|
||||
# syntax=docker/dockerfile:1.7
|
||||
# Multi-stage Angular console image with non-root runtime (DOCKER-44-001)
|
||||
ARG NODE_IMAGE=node:20-bullseye-slim
|
||||
ARG NGINX_IMAGE=nginxinc/nginx-unprivileged:1.27-alpine
|
||||
ARG APP_DIR=src/Web/StellaOps.Web
|
||||
ARG DIST_DIR=dist
|
||||
ARG APP_PORT=8080
|
||||
|
||||
FROM ${NODE_IMAGE} AS build
|
||||
ENV npm_config_fund=false npm_config_audit=false SOURCE_DATE_EPOCH=1704067200
|
||||
WORKDIR /app
|
||||
COPY ${APP_DIR}/package*.json ./
|
||||
RUN npm ci --prefer-offline --no-progress --cache .npm
|
||||
COPY ${APP_DIR}/ ./
|
||||
RUN npm run build -- --configuration=production --output-path=${DIST_DIR}
|
||||
|
||||
FROM ${NGINX_IMAGE} AS runtime
|
||||
ARG APP_PORT
|
||||
ENV APP_PORT=${APP_PORT}
|
||||
USER 101
|
||||
WORKDIR /
|
||||
COPY --from=build /app/${DIST_DIR}/ /usr/share/nginx/html/
|
||||
COPY ops/devops/docker/healthcheck-frontend.sh /usr/local/bin/healthcheck-frontend.sh
|
||||
RUN rm -f /etc/nginx/conf.d/default.conf && \
|
||||
cat > /etc/nginx/conf.d/default.conf <<CONF
|
||||
server {
|
||||
listen ${APP_PORT};
|
||||
listen [::]:${APP_PORT};
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
location / {
|
||||
try_files $$uri $$uri/ /index.html;
|
||||
}
|
||||
}
|
||||
CONF
|
||||
|
||||
EXPOSE ${APP_PORT}
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||
CMD /usr/local/bin/healthcheck-frontend.sh
|
||||
CMD ["nginx","-g","daemon off;"]
|
||||
172
devops/docker/Dockerfile.crypto-profile
Normal file
172
devops/docker/Dockerfile.crypto-profile
Normal file
@@ -0,0 +1,172 @@
|
||||
# syntax=docker/dockerfile:1.4
|
||||
# StellaOps Regional Crypto Profile
|
||||
# Selects regional cryptographic configuration at build time
|
||||
|
||||
# ============================================================================
|
||||
# Build Arguments
|
||||
# ============================================================================
|
||||
ARG CRYPTO_PROFILE=international
|
||||
ARG BASE_IMAGE=stellaops/platform:latest
|
||||
ARG SERVICE_NAME=authority
|
||||
|
||||
# ============================================================================
|
||||
# Regional Crypto Profile Layer
|
||||
# ============================================================================
|
||||
FROM ${BASE_IMAGE} AS regional-profile
|
||||
|
||||
# Copy regional cryptographic configuration
|
||||
ARG CRYPTO_PROFILE
|
||||
COPY etc/appsettings.crypto.${CRYPTO_PROFILE}.yaml /app/etc/appsettings.crypto.yaml
|
||||
COPY etc/crypto-plugins-manifest.json /app/etc/crypto-plugins-manifest.json
|
||||
|
||||
# Set environment variable for runtime verification
|
||||
ENV STELLAOPS_CRYPTO_PROFILE=${CRYPTO_PROFILE}
|
||||
ENV STELLAOPS_CRYPTO_CONFIG_PATH=/app/etc/appsettings.crypto.yaml
|
||||
ENV STELLAOPS_CRYPTO_MANIFEST_PATH=/app/etc/crypto-plugins-manifest.json
|
||||
|
||||
# Add labels for metadata
|
||||
LABEL com.stellaops.crypto.profile="${CRYPTO_PROFILE}"
|
||||
LABEL com.stellaops.crypto.config="/app/etc/appsettings.crypto.${CRYPTO_PROFILE}.yaml"
|
||||
LABEL com.stellaops.crypto.runtime-selection="true"
|
||||
|
||||
# ============================================================================
|
||||
# Service-Specific Regional Images
|
||||
# ============================================================================
|
||||
|
||||
# Authority with Regional Crypto
|
||||
FROM regional-profile AS authority
|
||||
WORKDIR /app/authority
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Authority.WebService.dll"]
|
||||
|
||||
# Signer with Regional Crypto
|
||||
FROM regional-profile AS signer
|
||||
WORKDIR /app/signer
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Signer.WebService.dll"]
|
||||
|
||||
# Attestor with Regional Crypto
|
||||
FROM regional-profile AS attestor
|
||||
WORKDIR /app/attestor
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Attestor.WebService.dll"]
|
||||
|
||||
# Concelier with Regional Crypto
|
||||
FROM regional-profile AS concelier
|
||||
WORKDIR /app/concelier
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Concelier.WebService.dll"]
|
||||
|
||||
# Scanner with Regional Crypto
|
||||
FROM regional-profile AS scanner
|
||||
WORKDIR /app/scanner
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Scanner.WebService.dll"]
|
||||
|
||||
# Excititor with Regional Crypto
|
||||
FROM regional-profile AS excititor
|
||||
WORKDIR /app/excititor
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Excititor.WebService.dll"]
|
||||
|
||||
# Policy with Regional Crypto
|
||||
FROM regional-profile AS policy
|
||||
WORKDIR /app/policy
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Policy.WebService.dll"]
|
||||
|
||||
# Scheduler with Regional Crypto
|
||||
FROM regional-profile AS scheduler
|
||||
WORKDIR /app/scheduler
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Scheduler.WebService.dll"]
|
||||
|
||||
# Notify with Regional Crypto
|
||||
FROM regional-profile AS notify
|
||||
WORKDIR /app/notify
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Notify.WebService.dll"]
|
||||
|
||||
# Zastava with Regional Crypto
|
||||
FROM regional-profile AS zastava
|
||||
WORKDIR /app/zastava
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Zastava.WebService.dll"]
|
||||
|
||||
# Gateway with Regional Crypto
|
||||
FROM regional-profile AS gateway
|
||||
WORKDIR /app/gateway
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Gateway.WebService.dll"]
|
||||
|
||||
# AirGap Importer with Regional Crypto
|
||||
FROM regional-profile AS airgap-importer
|
||||
WORKDIR /app/airgap-importer
|
||||
ENTRYPOINT ["dotnet", "StellaOps.AirGap.Importer.dll"]
|
||||
|
||||
# AirGap Exporter with Regional Crypto
|
||||
FROM regional-profile AS airgap-exporter
|
||||
WORKDIR /app/airgap-exporter
|
||||
ENTRYPOINT ["dotnet", "StellaOps.AirGap.Exporter.dll"]
|
||||
|
||||
# CLI with Regional Crypto
|
||||
FROM regional-profile AS cli
|
||||
WORKDIR /app/cli
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Cli.dll"]
|
||||
|
||||
# ============================================================================
|
||||
# Build Instructions
|
||||
# ============================================================================
|
||||
# Build international profile (default):
|
||||
# docker build -f deploy/docker/Dockerfile.crypto-profile \
|
||||
# --build-arg CRYPTO_PROFILE=international \
|
||||
# --target authority \
|
||||
# -t stellaops/authority:international .
|
||||
#
|
||||
# Build Russia (GOST) profile:
|
||||
# docker build -f deploy/docker/Dockerfile.crypto-profile \
|
||||
# --build-arg CRYPTO_PROFILE=russia \
|
||||
# --target scanner \
|
||||
# -t stellaops/scanner:russia .
|
||||
#
|
||||
# Build EU (eIDAS) profile:
|
||||
# docker build -f deploy/docker/Dockerfile.crypto-profile \
|
||||
# --build-arg CRYPTO_PROFILE=eu \
|
||||
# --target signer \
|
||||
# -t stellaops/signer:eu .
|
||||
#
|
||||
# Build China (SM) profile:
|
||||
# docker build -f deploy/docker/Dockerfile.crypto-profile \
|
||||
# --build-arg CRYPTO_PROFILE=china \
|
||||
# --target attestor \
|
||||
# -t stellaops/attestor:china .
|
||||
#
|
||||
# ============================================================================
|
||||
# Regional Profile Descriptions
|
||||
# ============================================================================
|
||||
# international: Default NIST algorithms (ES256, RS256, SHA-256)
|
||||
# Uses offline-verification plugin
|
||||
# Jurisdiction: world
|
||||
#
|
||||
# russia: GOST R 34.10-2012, GOST R 34.11-2012
|
||||
# Uses CryptoPro CSP plugin
|
||||
# Jurisdiction: russia
|
||||
# Requires: CryptoPro CSP SDK
|
||||
#
|
||||
# eu: eIDAS-compliant qualified trust services
|
||||
# Uses eIDAS plugin with qualified certificates
|
||||
# Jurisdiction: eu
|
||||
# Requires: eIDAS trust service provider integration
|
||||
#
|
||||
# china: SM2, SM3, SM4 algorithms
|
||||
# Uses SM crypto plugin
|
||||
# Jurisdiction: china
|
||||
# Requires: GmSSL or BouncyCastle SM extensions
|
||||
#
|
||||
# ============================================================================
|
||||
# Runtime Configuration
|
||||
# ============================================================================
|
||||
# The crypto provider is selected at runtime based on:
|
||||
# 1. STELLAOPS_CRYPTO_PROFILE environment variable
|
||||
# 2. /app/etc/appsettings.crypto.yaml configuration file
|
||||
# 3. /app/etc/crypto-plugins-manifest.json plugin metadata
|
||||
#
|
||||
# Plugin loading sequence:
|
||||
# 1. Application starts
|
||||
# 2. CryptoPluginLoader reads /app/etc/appsettings.crypto.yaml
|
||||
# 3. Loads enabled plugins from manifest
|
||||
# 4. Validates platform compatibility
|
||||
# 5. Validates jurisdiction compliance
|
||||
# 6. Registers providers with DI container
|
||||
# 7. Application uses ICryptoProvider abstraction
|
||||
#
|
||||
# No cryptographic code is executed until runtime plugin selection completes.
|
||||
56
devops/docker/Dockerfile.hardened.template
Normal file
56
devops/docker/Dockerfile.hardened.template
Normal file
@@ -0,0 +1,56 @@
|
||||
# syntax=docker/dockerfile:1.7
|
||||
# Hardened multi-stage template for StellaOps services
|
||||
# Parameters are build-time ARGs so this file can be re-used across services.
|
||||
|
||||
ARG SDK_IMAGE=mcr.microsoft.com/dotnet/sdk:10.0-bookworm-slim
|
||||
ARG RUNTIME_IMAGE=mcr.microsoft.com/dotnet/aspnet:10.0-bookworm-slim
|
||||
ARG APP_PROJECT=src/Service/Service.csproj
|
||||
ARG CONFIGURATION=Release
|
||||
ARG PUBLISH_DIR=/app/publish
|
||||
ARG APP_BINARY=StellaOps.Service
|
||||
ARG APP_USER=stella
|
||||
ARG APP_UID=10001
|
||||
ARG APP_GID=10001
|
||||
ARG APP_PORT=8080
|
||||
|
||||
FROM ${SDK_IMAGE} AS build
|
||||
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 \
|
||||
DOTNET_NOLOGO=1 \
|
||||
SOURCE_DATE_EPOCH=1704067200
|
||||
WORKDIR /src
|
||||
# Expect restore sources to be available offline via local-nugets/
|
||||
COPY . .
|
||||
RUN dotnet restore ${APP_PROJECT} --packages /src/local-nugets && \
|
||||
dotnet publish ${APP_PROJECT} -c ${CONFIGURATION} -o ${PUBLISH_DIR} \
|
||||
/p:UseAppHost=true /p:PublishTrimmed=false
|
||||
|
||||
FROM ${RUNTIME_IMAGE} AS runtime
|
||||
# Create non-root user/group with stable ids for auditability
|
||||
RUN groupadd -r -g ${APP_GID} ${APP_USER} && \
|
||||
useradd -r -u ${APP_UID} -g ${APP_GID} -d /var/lib/${APP_USER} ${APP_USER} && \
|
||||
mkdir -p /app /var/lib/${APP_USER} /var/run/${APP_USER} /tmp && \
|
||||
chown -R ${APP_UID}:${APP_GID} /app /var/lib/${APP_USER} /var/run/${APP_USER} /tmp
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=build --chown=${APP_UID}:${APP_GID} ${PUBLISH_DIR}/ ./
|
||||
# Ship healthcheck helper; callers may override with their own script
|
||||
COPY --chown=${APP_UID}:${APP_GID} ops/devops/docker/healthcheck.sh /usr/local/bin/healthcheck.sh
|
||||
|
||||
ENV ASPNETCORE_URLS=http://+:${APP_PORT} \
|
||||
DOTNET_EnableDiagnostics=0 \
|
||||
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 \
|
||||
COMPlus_EnableDiagnostics=0 \
|
||||
APP_BINARY=${APP_BINARY}
|
||||
|
||||
USER ${APP_UID}:${APP_GID}
|
||||
EXPOSE ${APP_PORT}
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
|
||||
CMD /usr/local/bin/healthcheck.sh
|
||||
|
||||
# Harden filesystem; deploys should also set readOnlyRootFilesystem true
|
||||
RUN chmod 500 /app && \
|
||||
find /app -maxdepth 1 -type f -exec chmod 400 {} \; && \
|
||||
find /app -maxdepth 1 -type d -exec chmod 500 {} \;
|
||||
|
||||
# Use shell form so APP_BINARY env can be expanded without duplicating the template per service
|
||||
ENTRYPOINT ["sh","-c","exec ./\"$APP_BINARY\""]
|
||||
212
devops/docker/Dockerfile.platform
Normal file
212
devops/docker/Dockerfile.platform
Normal file
@@ -0,0 +1,212 @@
|
||||
# syntax=docker/dockerfile:1.4
|
||||
# StellaOps Platform Image - Build Once, Deploy Everywhere
|
||||
# Builds ALL crypto plugins unconditionally for runtime selection
|
||||
|
||||
# ============================================================================
|
||||
# Stage 1: SDK Build - Build ALL Projects and Crypto Plugins
|
||||
# ============================================================================
|
||||
FROM mcr.microsoft.com/dotnet/sdk:10.0-preview AS build
|
||||
WORKDIR /src
|
||||
|
||||
# Copy solution and project files for dependency restore
|
||||
COPY Directory.Build.props Directory.Build.targets nuget.config ./
|
||||
COPY src/StellaOps.sln ./src/
|
||||
|
||||
# Copy all crypto plugin projects
|
||||
COPY src/__Libraries/StellaOps.Cryptography/ ./src/__Libraries/StellaOps.Cryptography/
|
||||
COPY src/__Libraries/StellaOps.Cryptography.DependencyInjection/ ./src/__Libraries/StellaOps.Cryptography.DependencyInjection/
|
||||
COPY src/__Libraries/StellaOps.Cryptography.PluginLoader/ ./src/__Libraries/StellaOps.Cryptography.PluginLoader/
|
||||
|
||||
# Crypto plugins - ALL built unconditionally
|
||||
COPY src/__Libraries/StellaOps.Cryptography.Plugin.OfflineVerification/ ./src/__Libraries/StellaOps.Cryptography.Plugin.OfflineVerification/
|
||||
# Note: Additional crypto plugins can be added here when available:
|
||||
# COPY src/__Libraries/StellaOps.Cryptography.Plugin.eIDAS/ ./src/__Libraries/StellaOps.Cryptography.Plugin.eIDAS/
|
||||
# COPY src/__Libraries/StellaOps.Cryptography.Plugin.CryptoPro/ ./src/__Libraries/StellaOps.Cryptography.Plugin.CryptoPro/
|
||||
# COPY src/__Libraries/StellaOps.Cryptography.Plugin.SM/ ./src/__Libraries/StellaOps.Cryptography.Plugin.SM/
|
||||
|
||||
# Copy all module projects
|
||||
COPY src/Authority/ ./src/Authority/
|
||||
COPY src/Signer/ ./src/Signer/
|
||||
COPY src/Attestor/ ./src/Attestor/
|
||||
COPY src/Concelier/ ./src/Concelier/
|
||||
COPY src/Scanner/ ./src/Scanner/
|
||||
COPY src/AirGap/ ./src/AirGap/
|
||||
COPY src/Excititor/ ./src/Excititor/
|
||||
COPY src/Policy/ ./src/Policy/
|
||||
COPY src/Scheduler/ ./src/Scheduler/
|
||||
COPY src/Notify/ ./src/Notify/
|
||||
COPY src/Zastava/ ./src/Zastava/
|
||||
COPY src/Gateway/ ./src/Gateway/
|
||||
COPY src/Cli/ ./src/Cli/
|
||||
|
||||
# Copy shared libraries
|
||||
COPY src/__Libraries/ ./src/__Libraries/
|
||||
|
||||
# Restore dependencies
|
||||
RUN dotnet restore src/StellaOps.sln
|
||||
|
||||
# Build entire solution (Release configuration)
|
||||
RUN dotnet build src/StellaOps.sln --configuration Release --no-restore
|
||||
|
||||
# Publish all web services and libraries
|
||||
# This creates /app/publish with all assemblies including crypto plugins
|
||||
RUN dotnet publish src/Authority/StellaOps.Authority.WebService/StellaOps.Authority.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/authority
|
||||
|
||||
RUN dotnet publish src/Signer/StellaOps.Signer.WebService/StellaOps.Signer.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/signer
|
||||
|
||||
RUN dotnet publish src/Attestor/StellaOps.Attestor.WebService/StellaOps.Attestor.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/attestor
|
||||
|
||||
RUN dotnet publish src/Concelier/StellaOps.Concelier.WebService/StellaOps.Concelier.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/concelier
|
||||
|
||||
RUN dotnet publish src/Scanner/StellaOps.Scanner.WebService/StellaOps.Scanner.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/scanner
|
||||
|
||||
RUN dotnet publish src/Excititor/StellaOps.Excititor.WebService/StellaOps.Excititor.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/excititor
|
||||
|
||||
RUN dotnet publish src/Policy/StellaOps.Policy.WebService/StellaOps.Policy.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/policy
|
||||
|
||||
RUN dotnet publish src/Scheduler/StellaOps.Scheduler.WebService/StellaOps.Scheduler.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/scheduler
|
||||
|
||||
RUN dotnet publish src/Notify/StellaOps.Notify.WebService/StellaOps.Notify.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/notify
|
||||
|
||||
RUN dotnet publish src/Zastava/StellaOps.Zastava.WebService/StellaOps.Zastava.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/zastava
|
||||
|
||||
RUN dotnet publish src/Gateway/StellaOps.Gateway.WebService/StellaOps.Gateway.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/gateway
|
||||
|
||||
RUN dotnet publish src/AirGap/StellaOps.AirGap.Importer/StellaOps.AirGap.Importer.csproj \
|
||||
--configuration Release --no-build --output /app/publish/airgap-importer
|
||||
|
||||
RUN dotnet publish src/AirGap/StellaOps.AirGap.Exporter/StellaOps.AirGap.Exporter.csproj \
|
||||
--configuration Release --no-build --output /app/publish/airgap-exporter
|
||||
|
||||
RUN dotnet publish src/Cli/StellaOps.Cli/StellaOps.Cli.csproj \
|
||||
--configuration Release --no-build --output /app/publish/cli
|
||||
|
||||
# Copy crypto plugin manifest
|
||||
COPY etc/crypto-plugins-manifest.json /app/publish/etc/
|
||||
|
||||
# ============================================================================
|
||||
# Stage 2: Runtime Base - Contains ALL Crypto Plugins
|
||||
# ============================================================================
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:10.0-preview AS runtime-base
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies for crypto providers
|
||||
# PostgreSQL client for Authority/Concelier/etc
|
||||
RUN apt-get update && apt-get install -y \
|
||||
postgresql-client \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy all published assemblies (includes all crypto plugins)
|
||||
COPY --from=build /app/publish /app/
|
||||
|
||||
# Expose common ports (these can be overridden by docker-compose)
|
||||
EXPOSE 8080 8443
|
||||
|
||||
# Labels
|
||||
LABEL com.stellaops.image.type="platform"
|
||||
LABEL com.stellaops.image.variant="all-plugins"
|
||||
LABEL com.stellaops.crypto.plugins="offline-verification"
|
||||
# Additional plugins will be added as they become available:
|
||||
# LABEL com.stellaops.crypto.plugins="offline-verification,eidas,cryptopro,sm"
|
||||
|
||||
# Health check placeholder (can be overridden per service)
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD curl -f http://localhost:8080/health || exit 1
|
||||
|
||||
# ============================================================================
|
||||
# Service-Specific Final Stages
|
||||
# ============================================================================
|
||||
|
||||
# Authority Service
|
||||
FROM runtime-base AS authority
|
||||
WORKDIR /app/authority
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Authority.WebService.dll"]
|
||||
|
||||
# Signer Service
|
||||
FROM runtime-base AS signer
|
||||
WORKDIR /app/signer
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Signer.WebService.dll"]
|
||||
|
||||
# Attestor Service
|
||||
FROM runtime-base AS attestor
|
||||
WORKDIR /app/attestor
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Attestor.WebService.dll"]
|
||||
|
||||
# Concelier Service
|
||||
FROM runtime-base AS concelier
|
||||
WORKDIR /app/concelier
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Concelier.WebService.dll"]
|
||||
|
||||
# Scanner Service
|
||||
FROM runtime-base AS scanner
|
||||
WORKDIR /app/scanner
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Scanner.WebService.dll"]
|
||||
|
||||
# Excititor Service
|
||||
FROM runtime-base AS excititor
|
||||
WORKDIR /app/excititor
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Excititor.WebService.dll"]
|
||||
|
||||
# Policy Service
|
||||
FROM runtime-base AS policy
|
||||
WORKDIR /app/policy
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Policy.WebService.dll"]
|
||||
|
||||
# Scheduler Service
|
||||
FROM runtime-base AS scheduler
|
||||
WORKDIR /app/scheduler
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Scheduler.WebService.dll"]
|
||||
|
||||
# Notify Service
|
||||
FROM runtime-base AS notify
|
||||
WORKDIR /app/notify
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Notify.WebService.dll"]
|
||||
|
||||
# Zastava Service
|
||||
FROM runtime-base AS zastava
|
||||
WORKDIR /app/zastava
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Zastava.WebService.dll"]
|
||||
|
||||
# Gateway Service
|
||||
FROM runtime-base AS gateway
|
||||
WORKDIR /app/gateway
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Gateway.WebService.dll"]
|
||||
|
||||
# AirGap Importer (CLI tool)
|
||||
FROM runtime-base AS airgap-importer
|
||||
WORKDIR /app/airgap-importer
|
||||
ENTRYPOINT ["dotnet", "StellaOps.AirGap.Importer.dll"]
|
||||
|
||||
# AirGap Exporter (CLI tool)
|
||||
FROM runtime-base AS airgap-exporter
|
||||
WORKDIR /app/airgap-exporter
|
||||
ENTRYPOINT ["dotnet", "StellaOps.AirGap.Exporter.dll"]
|
||||
|
||||
# CLI Tool
|
||||
FROM runtime-base AS cli
|
||||
WORKDIR /app/cli
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Cli.dll"]
|
||||
|
||||
# ============================================================================
|
||||
# Build Instructions
|
||||
# ============================================================================
|
||||
# Build platform image:
|
||||
# docker build -f deploy/docker/Dockerfile.platform --target runtime-base -t stellaops/platform:latest .
|
||||
#
|
||||
# Build specific service:
|
||||
# docker build -f deploy/docker/Dockerfile.platform --target authority -t stellaops/authority:latest .
|
||||
# docker build -f deploy/docker/Dockerfile.platform --target scanner -t stellaops/scanner:latest .
|
||||
#
|
||||
# The platform image contains ALL crypto plugins.
|
||||
# Regional selection happens at runtime via configuration (see Dockerfile.crypto-profile).
|
||||
76
devops/docker/base-image-guidelines.md
Normal file
76
devops/docker/base-image-guidelines.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Docker hardening blueprint (DOCKER-44-001)
|
||||
|
||||
Use this template for core services (API, Console, Orchestrator, Task Runner, Concelier, Excititor, Policy, Notify, Export, AdvisoryAI).
|
||||
|
||||
The reusable multi-stage scaffold lives at `ops/devops/docker/Dockerfile.hardened.template` and expects:
|
||||
- .NET 10 SDK/runtime images provided via offline mirror (`SDK_IMAGE` / `RUNTIME_IMAGE`).
|
||||
- `APP_PROJECT` path to the service csproj.
|
||||
- `healthcheck.sh` copied from `ops/devops/docker/` (already referenced by the template).
|
||||
- Optional: `APP_BINARY` (assembly name, defaults to `StellaOps.Service`) and `APP_PORT`.
|
||||
|
||||
Copy the template next to the service and set build args in CI (per-service matrix) to avoid maintaining divergent Dockerfiles.
|
||||
|
||||
```Dockerfile
|
||||
# syntax=docker/dockerfile:1.7
|
||||
ARG SDK_IMAGE=mcr.microsoft.com/dotnet/sdk:10.0-bookworm-slim
|
||||
ARG RUNTIME_IMAGE=mcr.microsoft.com/dotnet/aspnet:10.0-bookworm-slim
|
||||
ARG APP_PROJECT=src/Service/Service.csproj
|
||||
ARG CONFIGURATION=Release
|
||||
ARG APP_USER=stella
|
||||
ARG APP_UID=10001
|
||||
ARG APP_GID=10001
|
||||
ARG APP_PORT=8080
|
||||
|
||||
FROM ${SDK_IMAGE} AS build
|
||||
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 DOTNET_NOLOGO=1 SOURCE_DATE_EPOCH=1704067200
|
||||
WORKDIR /src
|
||||
COPY . .
|
||||
RUN dotnet restore ${APP_PROJECT} --packages /src/local-nugets && \
|
||||
dotnet publish ${APP_PROJECT} -c ${CONFIGURATION} -o /app/publish /p:UseAppHost=true /p:PublishTrimmed=false
|
||||
|
||||
FROM ${RUNTIME_IMAGE} AS runtime
|
||||
RUN groupadd -r -g ${APP_GID} ${APP_USER} && \
|
||||
useradd -r -u ${APP_UID} -g ${APP_GID} -d /var/lib/${APP_USER} ${APP_USER}
|
||||
WORKDIR /app
|
||||
COPY --from=build --chown=${APP_UID}:${APP_GID} /app/publish/ ./
|
||||
COPY --chown=${APP_UID}:${APP_GID} ops/devops/docker/healthcheck.sh /usr/local/bin/healthcheck.sh
|
||||
ENV ASPNETCORE_URLS=http://+:${APP_PORT} \
|
||||
DOTNET_EnableDiagnostics=0 \
|
||||
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 \
|
||||
COMPlus_EnableDiagnostics=0
|
||||
USER ${APP_UID}:${APP_GID}
|
||||
EXPOSE ${APP_PORT}
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 CMD /usr/local/bin/healthcheck.sh
|
||||
RUN chmod 500 /app && find /app -maxdepth 1 -type f -exec chmod 400 {} \; && find /app -maxdepth 1 -type d -exec chmod 500 {} \;
|
||||
ENTRYPOINT ["sh","-c","exec ./\"$APP_BINARY\""]
|
||||
```
|
||||
|
||||
Build stage (per service) should:
|
||||
- Use `mcr.microsoft.com/dotnet/sdk:10.0-bookworm-slim` (or mirror) with `DOTNET_CLI_TELEMETRY_OPTOUT=1`.
|
||||
- Restore from `local-nugets/` (offline) and run `dotnet publish -c Release -o /app/out`.
|
||||
- Set `SOURCE_DATE_EPOCH` to freeze timestamps.
|
||||
|
||||
Required checks:
|
||||
- No `root` user in final image.
|
||||
- `CAP_NET_RAW` dropped (default with non-root).
|
||||
- Read-only rootfs enforced at deploy time (`securityContext.readOnlyRootFilesystem: true` in Helm/Compose).
|
||||
- Health endpoints exposed: `/health/liveness`, `/health/readiness`, `/version`, `/metrics`.
|
||||
- Image SBOM generated (syft) in pipeline; attach cosign attestations (see DOCKER-44-002).
|
||||
|
||||
Service matrix & helper:
|
||||
- Build args for the core services are enumerated in `ops/devops/docker/services-matrix.env` (API, Console, Orchestrator, Task Runner, Concelier, Excititor, Policy, Notify, Export, AdvisoryAI).
|
||||
- `ops/devops/docker/build-all.sh` reads the matrix and builds/tag images from the shared template with consistent non-root/health defaults. Override `REGISTRY` and `TAG_SUFFIX` to publish.
|
||||
|
||||
Console (Angular) image:
|
||||
- Use `ops/devops/docker/Dockerfile.console` for the UI (Angular v17). It builds with `node:20-bullseye-slim`, serves via `nginxinc/nginx-unprivileged`, includes `healthcheck-frontend.sh`, and runs as non-root UID 101. Build with `docker build -f ops/devops/docker/Dockerfile.console --build-arg APP_DIR=src/Web/StellaOps.Web .`.
|
||||
|
||||
SBOM & attestation helper (DOCKER-44-002):
|
||||
- Script: `ops/devops/docker/sbom_attest.sh <image> [out-dir] [cosign-key]`
|
||||
- Emits SPDX (`*.spdx.json`) and CycloneDX (`*.cdx.json`) with `SOURCE_DATE_EPOCH` pinned for reproducibility.
|
||||
- Attaches both as cosign attestations (`--type spdx` / `--type cyclonedx`); supports keyless when `COSIGN_EXPERIMENTAL=1` or explicit PEM key.
|
||||
- Integrate in CI after image build/push; keep registry creds offline-friendly (use local registry mirror during air-gapped builds).
|
||||
|
||||
Health endpoint verification (DOCKER-44-003):
|
||||
- Script: `ops/devops/docker/verify_health_endpoints.sh <image> [port]` spins container, checks `/health/liveness`, `/health/readiness`, `/version`, `/metrics`, and warns if `/capabilities.merge` is not `false` (for Concelier/Excititor).
|
||||
- Run in CI after publishing the image; requires `docker` and `curl` (or `wget`).
|
||||
- Endpoint contract and ASP.NET wiring examples live in `ops/devops/docker/health-endpoints.md`; service owners should copy the snippet and ensure readiness checks cover DB/cache/bus.
|
||||
50
devops/docker/build-all.sh
Normal file
50
devops/docker/build-all.sh
Normal file
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build hardened images for the core services using the shared template/matrix (DOCKER-44-001)
|
||||
set -euo pipefail
|
||||
|
||||
ROOT=${ROOT:-"$(git rev-parse --show-toplevel)"}
|
||||
MATRIX=${MATRIX:-"${ROOT}/ops/devops/docker/services-matrix.env"}
|
||||
REGISTRY=${REGISTRY:-"stellaops"}
|
||||
TAG_SUFFIX=${TAG_SUFFIX:-"dev"}
|
||||
SDK_IMAGE=${SDK_IMAGE:-"mcr.microsoft.com/dotnet/sdk:10.0-bookworm-slim"}
|
||||
RUNTIME_IMAGE=${RUNTIME_IMAGE:-"mcr.microsoft.com/dotnet/aspnet:10.0-bookworm-slim"}
|
||||
|
||||
if [[ ! -f "${MATRIX}" ]]; then
|
||||
echo "matrix file not found: ${MATRIX}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Building services from ${MATRIX} -> ${REGISTRY}/<service>:${TAG_SUFFIX}" >&2
|
||||
|
||||
while IFS='|' read -r service dockerfile project binary port; do
|
||||
[[ -z "${service}" || "${service}" =~ ^# ]] && continue
|
||||
image="${REGISTRY}/${service}:${TAG_SUFFIX}"
|
||||
df_path="${ROOT}/${dockerfile}"
|
||||
if [[ ! -f "${df_path}" ]]; then
|
||||
echo "skipping ${service}: dockerfile missing (${df_path})" >&2
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ "${dockerfile}" == *"Dockerfile.console"* ]]; then
|
||||
# Angular console build uses its dedicated Dockerfile
|
||||
echo "[console] ${service} -> ${image}" >&2
|
||||
docker build \
|
||||
-f "${df_path}" "${ROOT}" \
|
||||
--build-arg APP_DIR="${project}" \
|
||||
--build-arg APP_PORT="${port}" \
|
||||
-t "${image}"
|
||||
else
|
||||
echo "[service] ${service} -> ${image}" >&2
|
||||
docker build \
|
||||
-f "${df_path}" "${ROOT}" \
|
||||
--build-arg SDK_IMAGE="${SDK_IMAGE}" \
|
||||
--build-arg RUNTIME_IMAGE="${RUNTIME_IMAGE}" \
|
||||
--build-arg APP_PROJECT="${project}" \
|
||||
--build-arg APP_BINARY="${binary}" \
|
||||
--build-arg APP_PORT="${port}" \
|
||||
-t "${image}"
|
||||
fi
|
||||
|
||||
done < "${MATRIX}"
|
||||
|
||||
echo "Build complete. Remember to enforce readOnlyRootFilesystem at deploy time and run sbom_attest.sh (DOCKER-44-002)." >&2
|
||||
44
devops/docker/health-endpoints.md
Normal file
44
devops/docker/health-endpoints.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Health & capability endpoint contract (DOCKER-44-003)
|
||||
|
||||
Target services: API, Console, Orchestrator, Task Runner, Concelier, Excititor, Policy, Notify, Export, AdvisoryAI.
|
||||
|
||||
## HTTP paths
|
||||
- `GET /health/liveness` — fast, dependency-free check; returns `200` and minimal body.
|
||||
- `GET /health/readiness` — may hit critical deps (DB, bus, cache); returns `503` when not ready.
|
||||
- `GET /version` — static payload with `service`, `version`, `commit`, `buildTimestamp` (ISO-8601 UTC), `source` (channel).
|
||||
- `GET /metrics` — Prometheus text exposition; reuse existing instrumentation.
|
||||
- `GET /capabilities` — if present for Concelier/Excititor, must include `"merge": false`.
|
||||
|
||||
## Minimal ASP.NET 10 wiring (per service)
|
||||
```csharp
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
// health checks; add real checks as needed
|
||||
builder.Services.AddHealthChecks();
|
||||
var app = builder.Build();
|
||||
|
||||
app.MapHealthChecks("/health/liveness", new() { Predicate = _ => false });
|
||||
app.MapHealthChecks("/health/readiness");
|
||||
|
||||
app.MapGet("/version", () => Results.Json(new {
|
||||
service = "StellaOps.Policy", // override per service
|
||||
version = ThisAssembly.AssemblyInformationalVersion,
|
||||
commit = ThisAssembly.Git.Commit,
|
||||
buildTimestamp = ThisAssembly.Git.CommitDate.UtcDateTime,
|
||||
source = Environment.GetEnvironmentVariable("STELLA_CHANNEL") ?? "edge"
|
||||
}));
|
||||
|
||||
app.UseHttpMetrics();
|
||||
app.MapMetrics();
|
||||
|
||||
app.Run();
|
||||
```
|
||||
- Ensure `ThisAssembly.*` source generators are enabled or substitute build vars.
|
||||
- Keep `/health/liveness` lightweight; `/health/readiness` should test critical dependencies (Mongo, Redis, message bus) with timeouts.
|
||||
- When adding `/capabilities`, explicitly emit `merge = false` for Concelier/Excititor.
|
||||
|
||||
## CI verification
|
||||
- After publishing an image, run `ops/devops/docker/verify_health_endpoints.sh <image> [port]`.
|
||||
- CI should fail if any required endpoint is missing or non-200.
|
||||
|
||||
## Deployment
|
||||
- Helm/Compose should set `readOnlyRootFilesystem: true` and wire readiness/liveness probes to these paths/port.
|
||||
10
devops/docker/healthcheck-frontend.sh
Normal file
10
devops/docker/healthcheck-frontend.sh
Normal file
@@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
HOST="${HEALTH_HOST:-127.0.0.1}"
|
||||
PORT="${HEALTH_PORT:-8080}"
|
||||
PATH_CHECK="${HEALTH_PATH:-/}"
|
||||
USER_AGENT="stellaops-frontend-healthcheck"
|
||||
|
||||
wget -qO- "http://${HOST}:${PORT}${PATH_CHECK}" \
|
||||
--header="User-Agent: ${USER_AGENT}" \
|
||||
--timeout="${HEALTH_TIMEOUT:-4}" >/dev/null
|
||||
24
devops/docker/healthcheck.sh
Normal file
24
devops/docker/healthcheck.sh
Normal file
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
HOST="${HEALTH_HOST:-127.0.0.1}"
|
||||
PORT="${HEALTH_PORT:-8080}"
|
||||
LIVENESS_PATH="${LIVENESS_PATH:-/health/liveness}"
|
||||
READINESS_PATH="${READINESS_PATH:-/health/readiness}"
|
||||
USER_AGENT="stellaops-healthcheck"
|
||||
|
||||
fetch() {
|
||||
target_path="$1"
|
||||
# BusyBox wget is available in Alpine; curl not assumed.
|
||||
wget -qO- "http://${HOST}:${PORT}${target_path}" \
|
||||
--header="User-Agent: ${USER_AGENT}" \
|
||||
--timeout="${HEALTH_TIMEOUT:-4}" >/dev/null
|
||||
}
|
||||
|
||||
fail=0
|
||||
if ! fetch "$LIVENESS_PATH"; then
|
||||
fail=1
|
||||
fi
|
||||
if ! fetch "$READINESS_PATH"; then
|
||||
fail=1
|
||||
fi
|
||||
exit "$fail"
|
||||
48
devops/docker/sbom_attest.sh
Normal file
48
devops/docker/sbom_attest.sh
Normal file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
# Deterministic SBOM + attestation helper for DOCKER-44-002
|
||||
# Usage: ./sbom_attest.sh <image-ref> [output-dir] [cosign-key]
|
||||
# - image-ref: fully qualified image (e.g., ghcr.io/stellaops/policy:1.2.3)
|
||||
# - output-dir: defaults to ./sbom
|
||||
# - cosign-key: path to cosign key (PEM). If omitted, uses keyless if allowed (COSIGN_EXPERIMENTAL=1)
|
||||
|
||||
set -euo pipefail
|
||||
IMAGE_REF=${1:?"image ref required"}
|
||||
OUT_DIR=${2:-sbom}
|
||||
COSIGN_KEY=${3:-}
|
||||
|
||||
mkdir -p "${OUT_DIR}"
|
||||
|
||||
# Normalize filename (replace / and : with _)
|
||||
name_safe() {
|
||||
echo "$1" | tr '/:' '__'
|
||||
}
|
||||
|
||||
BASENAME=$(name_safe "${IMAGE_REF}")
|
||||
SPDX_JSON="${OUT_DIR}/${BASENAME}.spdx.json"
|
||||
CDX_JSON="${OUT_DIR}/${BASENAME}.cdx.json"
|
||||
ATTESTATION="${OUT_DIR}/${BASENAME}.sbom.att"
|
||||
|
||||
# Freeze timestamps for reproducibility
|
||||
export SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH:-1704067200}
|
||||
|
||||
# Generate SPDX 3.0-ish JSON (syft formats are stable and offline-friendly)
|
||||
syft "${IMAGE_REF}" -o spdx-json > "${SPDX_JSON}"
|
||||
# Generate CycloneDX 1.6 JSON
|
||||
syft "${IMAGE_REF}" -o cyclonedx-json > "${CDX_JSON}"
|
||||
|
||||
# Attach SBOMs as cosign attestations (one per format)
|
||||
export COSIGN_EXPERIMENTAL=${COSIGN_EXPERIMENTAL:-1}
|
||||
COSIGN_ARGS=("attest" "--predicate" "${SPDX_JSON}" "--type" "spdx" "${IMAGE_REF}")
|
||||
if [[ -n "${COSIGN_KEY}" ]]; then
|
||||
COSIGN_ARGS+=("--key" "${COSIGN_KEY}")
|
||||
fi
|
||||
cosign "${COSIGN_ARGS[@]}"
|
||||
|
||||
COSIGN_ARGS=("attest" "--predicate" "${CDX_JSON}" "--type" "cyclonedx" "${IMAGE_REF}")
|
||||
if [[ -n "${COSIGN_KEY}" ]]; then
|
||||
COSIGN_ARGS+=("--key" "${COSIGN_KEY}")
|
||||
fi
|
||||
cosign "${COSIGN_ARGS[@]}"
|
||||
|
||||
echo "SBOMs written to ${SPDX_JSON} and ${CDX_JSON}" >&2
|
||||
echo "Attestations pushed for ${IMAGE_REF}" >&2
|
||||
12
devops/docker/services-matrix.env
Normal file
12
devops/docker/services-matrix.env
Normal file
@@ -0,0 +1,12 @@
|
||||
# service|dockerfile|project|binary|port
|
||||
# Paths are relative to repo root; dockerfile is usually the shared hardened template.
|
||||
api|ops/devops/docker/Dockerfile.hardened.template|src/VulnExplorer/StellaOps.VulnExplorer.Api/StellaOps.VulnExplorer.Api.csproj|StellaOps.VulnExplorer.Api|8080
|
||||
orchestrator|ops/devops/docker/Dockerfile.hardened.template|src/Orchestrator/StellaOps.Orchestrator.WebService/StellaOps.Orchestrator.WebService.csproj|StellaOps.Orchestrator.WebService|8080
|
||||
task-runner|ops/devops/docker/Dockerfile.hardened.template|src/Orchestrator/StellaOps.Orchestrator.Worker/StellaOps.Orchestrator.Worker.csproj|StellaOps.Orchestrator.Worker|8081
|
||||
concelier|ops/devops/docker/Dockerfile.hardened.template|src/Concelier/StellaOps.Concelier.WebService/StellaOps.Concelier.WebService.csproj|StellaOps.Concelier.WebService|8080
|
||||
excititor|ops/devops/docker/Dockerfile.hardened.template|src/Excititor/StellaOps.Excititor.WebService/StellaOps.Excititor.WebService.csproj|StellaOps.Excititor.WebService|8080
|
||||
policy|ops/devops/docker/Dockerfile.hardened.template|src/Policy/StellaOps.Policy.Gateway/StellaOps.Policy.Gateway.csproj|StellaOps.Policy.Gateway|8084
|
||||
notify|ops/devops/docker/Dockerfile.hardened.template|src/Notify/StellaOps.Notify.WebService/StellaOps.Notify.WebService.csproj|StellaOps.Notify.WebService|8080
|
||||
export|ops/devops/docker/Dockerfile.hardened.template|src/ExportCenter/StellaOps.ExportCenter.WebService/StellaOps.ExportCenter.WebService.csproj|StellaOps.ExportCenter.WebService|8080
|
||||
advisoryai|ops/devops/docker/Dockerfile.hardened.template|src/AdvisoryAI/StellaOps.AdvisoryAI.WebService/StellaOps.AdvisoryAI.WebService.csproj|StellaOps.AdvisoryAI.WebService|8080
|
||||
console|ops/devops/docker/Dockerfile.console|src/Web/StellaOps.Web|StellaOps.Web|8080
|
||||
70
devops/docker/verify_health_endpoints.sh
Normal file
70
devops/docker/verify_health_endpoints.sh
Normal file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env bash
|
||||
# Smoke-check /health and capability endpoints for a built image (DOCKER-44-003)
|
||||
# Usage: ./verify_health_endpoints.sh <image-ref> [port]
|
||||
# Requires: docker, curl or wget
|
||||
set -euo pipefail
|
||||
IMAGE=${1:?"image ref required"}
|
||||
PORT=${2:-8080}
|
||||
CONTAINER_NAME="healthcheck-$$"
|
||||
TIMEOUT=30
|
||||
SLEEP=1
|
||||
|
||||
have_curl=1
|
||||
if ! command -v curl >/dev/null 2>&1; then
|
||||
have_curl=0
|
||||
fi
|
||||
|
||||
req() {
|
||||
local path=$1
|
||||
local url="http://127.0.0.1:${PORT}${path}"
|
||||
if [[ $have_curl -eq 1 ]]; then
|
||||
curl -fsS --max-time 3 "$url" >/dev/null
|
||||
else
|
||||
wget -qO- --timeout=3 "$url" >/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
echo "[info] starting container ${IMAGE} on port ${PORT}" >&2
|
||||
cleanup
|
||||
if ! docker run -d --rm --name "$CONTAINER_NAME" -p "${PORT}:${PORT}" "$IMAGE" >/dev/null; then
|
||||
echo "[error] failed to start image ${IMAGE}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# wait for readiness
|
||||
start=$(date +%s)
|
||||
while true; do
|
||||
if req /health/liveness 2>/dev/null; then break; fi
|
||||
now=$(date +%s)
|
||||
if (( now - start > TIMEOUT )); then
|
||||
echo "[error] liveness endpoint did not come up in ${TIMEOUT}s" >&2
|
||||
exit 1
|
||||
fi
|
||||
sleep $SLEEP
|
||||
done
|
||||
|
||||
# verify endpoints
|
||||
fail=0
|
||||
for path in /health/liveness /health/readiness /version /metrics; do
|
||||
if ! req "$path"; then
|
||||
echo "[error] missing or failing ${path}" >&2
|
||||
fail=1
|
||||
fi
|
||||
done
|
||||
|
||||
# capability endpoint optional; if present ensure merge=false for Concelier/Excititor
|
||||
if req /capabilities 2>/dev/null; then
|
||||
body="$(curl -fsS "http://127.0.0.1:${PORT}/capabilities" 2>/dev/null || true)"
|
||||
if echo "$body" | grep -q '"merge"[[:space:]]*:[[:space:]]*false'; then
|
||||
:
|
||||
else
|
||||
echo "[warn] /capabilities present but merge flag not false" >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
exit $fail
|
||||
22
devops/docs/AGENTS.md
Normal file
22
devops/docs/AGENTS.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# DevOps & Release — Agent Charter
|
||||
|
||||
## Mission
|
||||
Execute deterministic build/release pipeline per `docs/modules/devops/ARCHITECTURE.md`:
|
||||
- Reproducible builds with SBOM/provenance, cosign signing, transparency logging.
|
||||
- Channel manifests (LTS/Stable/Edge) with digests, Helm/Compose profiles.
|
||||
- Performance guard jobs ensuring budgets.
|
||||
|
||||
## Expectations
|
||||
- Coordinate with Scanner/Scheduler/Notify teams for artifact availability.
|
||||
- Maintain CI reliability; update the owning sprint entries as states change.
|
||||
|
||||
## Required Reading
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/modules/airgap/airgap-mode.md`
|
||||
|
||||
## Working Agreement
|
||||
- 1. Update task status to `DOING`/`DONE` inside the corresponding `docs/implplan/SPRINT_*.md` entry when you start or finish work.
|
||||
- 2. Review this charter and the Required Reading documents before coding; confirm prerequisites are met.
|
||||
- 3. Keep changes deterministic (stable ordering, timestamps, hashes) and align with offline/air-gap expectations.
|
||||
- 4. Coordinate doc updates, tests, and cross-guild communication whenever contracts or workflows change.
|
||||
- 5. Revert to `TODO` if you pause the task without shipping changes; leave notes in commit/PR descriptions for context.
|
||||
28
devops/docs/README-space.md
Normal file
28
devops/docs/README-space.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Freeing Disk Space Quickly
|
||||
|
||||
If PTY allocation or builds fail with “No space left on device”, try these steps (safe order):
|
||||
|
||||
1) Remove build/test artefacts:
|
||||
```
|
||||
DRY_RUN=1 scripts/devops/cleanup-workspace.sh # preview
|
||||
SAFE_ONLY=0 scripts/devops/cleanup-workspace.sh # include bin/obj if needed
|
||||
```
|
||||
|
||||
2) Prune Docker cache (if allowed):
|
||||
```
|
||||
docker system prune -af
|
||||
docker volume prune -f
|
||||
```
|
||||
|
||||
3) Clear NuGet cache (local user):
|
||||
```
|
||||
rm -rf ~/.nuget/packages
|
||||
```
|
||||
|
||||
4) Remove old CI artefacts:
|
||||
- `ops/devops/artifacts/`
|
||||
- `ops/devops/ci-110-runner/artifacts/`
|
||||
- `ops/devops/sealed-mode-ci/artifacts/`
|
||||
- `out/` directories
|
||||
|
||||
5) Re-run the blocked workflow.
|
||||
27
devops/docs/TASKS.completed.md
Normal file
27
devops/docs/TASKS.completed.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Completed Tasks
|
||||
|
||||
| ID | Status | Owner(s) | Depends on | Description | Exit Criteria |
|
||||
|----|--------|----------|------------|-------------|---------------|
|
||||
| DEVOPS-HELM-09-001 | DONE | DevOps Guild | SCANNER-WEB-09-101 | Create Helm/Compose environment profiles (dev, staging, airgap) with deterministic digests. | Profiles committed under `deploy/`; docs updated; CI smoke deploy passes. |
|
||||
| DEVOPS-SCANNER-09-204 | DONE (2025-10-21) | DevOps Guild, Scanner WebService Guild | SCANNER-EVENTS-15-201 | Surface `SCANNER__EVENTS__*` environment variables across docker-compose (dev/stage/airgap) and Helm values, defaulting to share the Redis queue DSN. | Compose/Helm configs ship enabled Redis event publishing with documented overrides; lint jobs updated; docs cross-link to new knobs. |
|
||||
| DEVOPS-SCANNER-09-205 | DONE (2025-10-21) | DevOps Guild, Notify Guild | DEVOPS-SCANNER-09-204 | Add Notify smoke stage that tails the Redis stream and asserts `scanner.report.ready`/`scanner.scan.completed` reach Notify WebService in staging. | CI job reads Redis stream during scanner smoke deploy, confirms Notify ingestion via API, alerts on failure. |
|
||||
| DEVOPS-PERF-10-001 | DONE | DevOps Guild | BENCH-SCANNER-10-001 | Add perf smoke job (SBOM compose <5 s target) to CI. | CI job runs sample build verifying <5 s; alerts configured. |
|
||||
| DEVOPS-PERF-10-002 | DONE (2025-10-23) | DevOps Guild | BENCH-SCANNER-10-002 | Publish analyzer bench metrics to Grafana/perf workbook and alarm on ≥20 % regressions. | CI exports JSON for dashboards; Grafana panel wired; Ops on-call doc updated with alert hook. |
|
||||
| DEVOPS-REL-14-001 | DONE (2025-10-26) | DevOps Guild | SIGNER-API-11-101, ATTESTOR-API-11-201 | Deterministic build/release pipeline with SBOM/provenance, signing, manifest generation. | CI pipeline produces signed images + SBOM/attestations, manifests published with verified hashes, docs updated. |
|
||||
| DEVOPS-REL-14-004 | DONE (2025-10-26) | DevOps Guild, Scanner Guild | DEVOPS-REL-14-001, SCANNER-ANALYZERS-LANG-10-309P | Extend release/offline smoke jobs to exercise the Python analyzer plug-in (warm/cold scans, determinism, signature checks). | Release/Offline pipelines run Python analyzer smoke suite; alerts hooked; docs updated with new coverage matrix. |
|
||||
| DEVOPS-REL-17-002 | DONE (2025-10-26) | DevOps Guild | DEVOPS-REL-14-001, SCANNER-EMIT-17-701 | Persist stripped-debug artifacts organised by GNU build-id and bundle them into release/offline kits with checksum manifests. | CI job writes `.debug` files under `artifacts/debug/.build-id/`, manifest + checksums published, offline kit includes cache, smoke job proves symbol lookup via build-id. |
|
||||
| DEVOPS-MIRROR-08-001 | DONE (2025-10-19) | DevOps Guild | DEVOPS-REL-14-001 | Stand up managed mirror profiles for `*.stella-ops.org` (Concelier/Excititor), including Helm/Compose overlays, multi-tenant secrets, CDN caching, and sync documentation. | Infra overlays committed, CI smoke deploy hits mirror endpoints, runbooks published for downstream sync and quota management. |
|
||||
| DEVOPS-POLICY-20-001 | DONE (2025-10-26) | DevOps Guild, Policy Guild | POLICY-ENGINE-20-001 | Integrate DSL linting in CI (parser/compile) to block invalid policies; add pipeline step compiling sample policies. | CI fails on syntax errors; lint logs surfaced; docs updated with pipeline instructions. |
|
||||
| DEVOPS-POLICY-20-003 | DONE (2025-10-26) | DevOps Guild, QA Guild | DEVOPS-POLICY-20-001, POLICY-ENGINE-20-005 | Determinism CI: run Policy Engine twice with identical inputs and diff outputs to guard non-determinism. | CI job compares outputs, fails on differences, logs stored; documentation updated. |
|
||||
| DEVOPS-POLICY-20-004 | DONE (2025-10-27) | DevOps Guild, Scheduler Guild, CLI Guild | SCHED-MODELS-20-001, CLI-POLICY-20-002 | Automate policy schema exports: generate JSON Schema from `PolicyRun*` DTOs during CI, publish artefacts, and emit change alerts for CLI consumers (Slack + changelog). | CI stage outputs versioned schema files, uploads artefacts, notifies #policy-engine channel on change; docs/CLI references updated. |
|
||||
| DEVOPS-OBS-50-001 | DONE (2025-10-26) | DevOps Guild, Observability Guild | TELEMETRY-OBS-50-001 | Deliver default OpenTelemetry collector deployment (Compose/Helm manifests), OTLP ingestion endpoints, and secure pipeline (authN, mTLS, tenant partitioning). Provide smoke test verifying traces/logs/metrics ingestion. | Collector manifests committed; smoke test green; docs updated; imposed rule banner reminder noted. |
|
||||
| DEVOPS-OBS-50-003 | DONE (2025-10-26) | DevOps Guild, Offline Kit Guild | DEVOPS-OBS-50-001 | Package telemetry stack configs for air-gapped installs (Offline Kit bundle, documented overrides, sample values) and automate checksum/signature generation. | Offline bundle includes collector+storage configs; checksums published; docs cross-linked; imposed rule annotation recorded. |
|
||||
| DEVOPS-LAUNCH-18-100 | DONE (2025-10-26) | DevOps Guild | - | Finalise production environment footprint (clusters, secrets, network overlays) for full-platform go-live. | IaC/compose overlays committed, secrets placeholders documented, dry-run deploy succeeds in staging. |
|
||||
|
||||
| DEVOPS-CONSOLE-23-002 | TODO | DevOps Guild, Console Guild | DEVOPS-CONSOLE-23-001, CONSOLE-REL-23-301 | Produce `stella-console` container build + Helm chart overlays with deterministic digests, SBOM/provenance artefacts, and offline bundle packaging scripts. | Container published to registry mirror, Helm values committed, SBOM/attestations generated, offline kit job passes smoke test, docs updated. |
|
||||
| DEVOPS-LAUNCH-18-100 | DONE (2025-10-26) | DevOps Guild | - | Finalise production environment footprint (clusters, secrets, network overlays) for full-platform go-live. | IaC/compose overlays committed, secrets placeholders documented, dry-run deploy succeeds in staging. |
|
||||
| DEVOPS-LAUNCH-18-900 | DONE (2025-10-26) | DevOps Guild, Module Leads | Wave 0 completion | Collect “full implementation” sign-off from module owners and consolidate launch readiness checklist. | Sign-off record stored under `docs/modules/devops/runbooks/launch-readiness.md`; outstanding gaps triaged; checklist approved. |
|
||||
| DEVOPS-LAUNCH-18-001 | DONE (2025-10-26) | DevOps Guild | DEVOPS-LAUNCH-18-100, DEVOPS-LAUNCH-18-900 | Production launch cutover rehearsal and runbook publication. | `docs/modules/devops/runbooks/launch-cutover.md` drafted, rehearsal executed with rollback drill, approvals captured. |
|
||||
| DEVOPS-NUGET-13-001 | DONE (2025-10-25) | DevOps Guild, Platform Leads | DEVOPS-REL-14-001 | Add .NET 10 preview feeds / local mirrors so `Microsoft.Extensions.*` 10.0 preview packages restore offline; refresh restore docs. | NuGet.config maps preview feeds (or local mirrored packages), `dotnet restore` succeeds for Excititor/Concelier solutions without ad-hoc feed edits, docs updated for offline bootstrap. |
|
||||
| DEVOPS-NUGET-13-002 | DONE (2025-10-26) | DevOps Guild | DEVOPS-NUGET-13-001 | Ensure all solutions/projects prefer `local-nuget` before public sources and document restore order validation. | `NuGet.config` and solution-level configs resolve from `local-nuget` first; automated check verifies priority; docs updated for restore ordering. |
|
||||
| DEVOPS-NUGET-13-003 | DONE (2025-10-26) | DevOps Guild, Platform Leads | DEVOPS-NUGET-13-002 | Sweep `Microsoft.*` NuGet dependencies pinned to 8.* and upgrade to latest .NET 10 equivalents (or .NET 9 when 10 unavailable), updating restore guidance. | Dependency audit shows no 8.* `Microsoft.*` packages remaining; CI builds green; changelog/doc sections capture upgrade rationale. |
|
||||
74
devops/docs/deploy-readme.md
Normal file
74
devops/docs/deploy-readme.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# Deployment Profiles
|
||||
|
||||
This directory contains deterministic deployment bundles for the core Stella Ops stack. All manifests reference immutable image digests and map 1:1 to the release manifests stored under `deploy/releases/`.
|
||||
|
||||
## Structure
|
||||
|
||||
- `releases/` – canonical release manifests (edge, stable, airgap) used to source image digests.
|
||||
- `compose/` – Docker Compose bundles for dev/stage/airgap targets plus `.env` seed files.
|
||||
- `compose/docker-compose.mirror.yaml` – managed mirror bundle for `*.stella-ops.org` with gateway cache and multi-tenant auth.
|
||||
- `compose/docker-compose.telemetry.yaml` – optional OpenTelemetry collector overlay (mutual TLS, OTLP pipelines).
|
||||
- `compose/docker-compose.telemetry-storage.yaml` – optional Prometheus/Tempo/Loki stack for observability backends.
|
||||
- `helm/stellaops/` – multi-profile Helm chart with values files for dev/stage/airgap.
|
||||
- `helm/stellaops/INSTALL.md` – install/runbook for prod and airgap profiles with digest pins.
|
||||
- `telemetry/` – shared OpenTelemetry collector configuration and certificate artefacts (generated via tooling).
|
||||
- `tools/validate-profiles.sh` – helper that runs `docker compose config` and `helm lint/template` for every profile.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Update or add a release manifest under `releases/` with the new digests.
|
||||
2. Mirror the digests into the Compose and Helm profiles that correspond to that channel.
|
||||
3. Run `deploy/tools/validate-profiles.sh` (requires Docker CLI and Helm) to ensure the bundles lint and template cleanly.
|
||||
4. If telemetry ingest is required for the release, generate development certificates using
|
||||
`./ops/devops/telemetry/generate_dev_tls.sh` and run the collector smoke test with
|
||||
`python ./ops/devops/telemetry/smoke_otel_collector.py` to verify the OTLP endpoints.
|
||||
5. Commit the change alongside any documentation updates (e.g. install guide cross-links).
|
||||
|
||||
Maintaining the digest linkage keeps offline/air-gapped installs reproducible and avoids tag drift between environments.
|
||||
|
||||
### Surface.Env rollout warnings
|
||||
|
||||
- Compose (`deploy/compose/env/*.env.example`) and Helm (`deploy/helm/stellaops/values-*.yaml`) now seed `SCANNER_SURFACE_*` _and_ `ZASTAVA_SURFACE_*` variables so Scanner Worker/WebService and Zastava Observer/Webhook resolve cache roots, Surface.FS endpoints, and secrets providers through `StellaOps.Scanner.Surface.Env`.
|
||||
- During rollout, watch for structured log messages (and readiness output) prefixed with `surface.env.`—for example, `surface.env.cache_root_missing`, `surface.env.endpoint_unreachable`, or `surface.env.secrets_provider_invalid`.
|
||||
- Treat these warnings as deployment blockers: update the endpoint/cache/secrets values or permissions before promoting the environment, otherwise workers will fail fast at startup.
|
||||
- Air-gapped bundles default the secrets provider to `file` with `/etc/stellaops/secrets`; connected clusters default to `kubernetes`. Adjust the provider/root pair if your secrets manager differs.
|
||||
- Secret provisioning workflows for Kubernetes/Compose/Offline Kit are documented in `ops/devops/secrets/surface-secrets-provisioning.md`; follow that for `Surface.Secrets` handles and RBAC/permissions.
|
||||
|
||||
### Mongo2Go OpenSSL prerequisites
|
||||
|
||||
- Linux runners that execute Mongo2Go-backed suites (Excititor, Scheduler, Graph, etc.) must expose OpenSSL 1.1 (`libcrypto.so.1.1`, `libssl.so.1.1`). The canonical copies live under `tests/native/openssl-1.1/linux-x64`.
|
||||
- Export `LD_LIBRARY_PATH="$(git rev-parse --show-toplevel)/tests/native/openssl-1.1/linux-x64:${LD_LIBRARY_PATH:-}"` before invoking `dotnet test`. Example:\
|
||||
`LD_LIBRARY_PATH="$(pwd)/tests/native/openssl-1.1/linux-x64" dotnet test src/Excititor/__Tests/StellaOps.Excititor.WebService.Tests/StellaOps.Excititor.WebService.Tests.csproj --nologo`.
|
||||
- CI agents or Dockerfiles that host these tests should either mount the directory into the container or copy the two `.so` files into a directory that is already on the runtime library path.
|
||||
|
||||
### Additional tooling
|
||||
|
||||
- `deploy/tools/check-channel-alignment.py` – verifies that Helm/Compose profiles reference the exact images listed in a release manifest. Run it for each channel before promoting a release.
|
||||
- `ops/devops/telemetry/generate_dev_tls.sh` – produces local CA/server/client certificates for Compose-based collector testing.
|
||||
- `ops/devops/telemetry/smoke_otel_collector.py` – sends OTLP traffic and asserts the collector accepted traces, metrics, and logs.
|
||||
- `ops/devops/telemetry/package_offline_bundle.py` – packages telemetry assets (config/Helm/Compose) into a signed tarball for air-gapped installs.
|
||||
- `docs/modules/devops/runbooks/deployment-upgrade.md` – end-to-end instructions for upgrade, rollback, and channel promotion workflows (Helm + Compose).
|
||||
|
||||
### Tenancy observability & chaos (DEVOPS-TEN-49-001)
|
||||
|
||||
- Import `ops/devops/tenant/recording-rules.yaml` and `ops/devops/tenant/alerts.yaml` into your Prometheus rule groups.
|
||||
- Add Grafana dashboard `ops/devops/tenant/dashboards/tenant-audit.json` (folder `StellaOps / Tenancy`) to watch latency/error/auth cache ratios per tenant/service.
|
||||
- Run the multi-tenant k6 harness `ops/devops/tenant/k6-tenant-load.js` to hit 5k concurrent tenant-labelled requests (defaults to read/write 90/10, header `X-StellaOps-Tenant`).
|
||||
- Execute JWKS outage chaos via `ops/devops/tenant/jwks-chaos.sh` on an isolated agent with sudo/iptables; watch alerts `jwks_cache_miss_spike` and `tenant_auth_failures_spike` while load is active.
|
||||
|
||||
## CI smoke checks
|
||||
|
||||
The `.gitea/workflows/build-test-deploy.yml` pipeline includes a `notify-smoke` stage that validates scanner event propagation after staging deployments. Configure the following repository secrets (or environment-level secrets) so the job can connect to Redis and the Notify API:
|
||||
|
||||
- `NOTIFY_SMOKE_REDIS_DSN` – Redis connection string (`redis://user:pass@host:port/db`).
|
||||
- `NOTIFY_SMOKE_NOTIFY_BASEURL` – Base URL for the staging Notify WebService (e.g. `https://notify.stage.stella-ops.internal`).
|
||||
- `NOTIFY_SMOKE_NOTIFY_TOKEN` – OAuth bearer token (service account) with permission to read deliveries.
|
||||
- `NOTIFY_SMOKE_NOTIFY_TENANT` – Tenant identifier used for the smoke validation requests.
|
||||
- *(Optional)* `NOTIFY_SMOKE_NOTIFY_TENANT_HEADER` – Override for the tenant header name (defaults to `X-StellaOps-Tenant`).
|
||||
|
||||
Define the following repository variables (or secrets) to drive the assertions performed by the smoke check:
|
||||
|
||||
- `NOTIFY_SMOKE_EXPECT_KINDS` – Comma-separated event kinds the checker must observe (for example `scanner.report.ready,scanner.scan.completed`).
|
||||
- `NOTIFY_SMOKE_LOOKBACK_MINUTES` – Time window (in minutes) used when scanning the Redis stream for recent events (for example `30`).
|
||||
|
||||
All of the above values are required—the workflow fails fast with a descriptive error if any are missing or empty. Provide the variables at the organisation or repository scope before enabling the smoke stage.
|
||||
30
devops/docs/nuget-preview-packages.csv
Normal file
30
devops/docs/nuget-preview-packages.csv
Normal file
@@ -0,0 +1,30 @@
|
||||
# Package,Version,SHA256,SourceBase(optional)
|
||||
# DotNetPublicFlat=https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.AspNetCore.Authentication.JwtBearer,10.0.0-rc.2.25502.107,3223f447bde9a3620477305a89520e8becafe23b481a0b423552af572439f8c2,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.AspNetCore.Mvc.Testing,10.0.0-rc.2.25502.107,b6b53c62e0abefdca30e6ca08ab8357e395177dd9f368ab3ad4bbbd07e517229,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.AspNetCore.OpenApi,10.0.0-rc.2.25502.107,f64de1fe870306053346a31263e53e29f2fdfe0eae432a3156f8d7d705c81d85,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.Data.Sqlite,9.0.0-rc.1.24451.1,770b637317e1e924f1b13587b31af0787c8c668b1d9f53f2fccae8ee8704e167,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.Extensions.Caching.Memory,10.0.0-rc.2.25502.107,6ec6d156ed06b07cbee9fa1c0803b8d54a5f904a0bf0183172f87b63c4044426,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.Extensions.Configuration,10.0.0-rc.2.25502.107,0716f72cdc99b03946c98c418c39d42208fc65f20301bd1f26a6c174646870f6,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.Extensions.Configuration.Abstractions,10.0.0-rc.2.25502.107,db6e2cd37c40b5ac5ca7a4f40f5edafda2b6a8690f95a8c64b54c777a1d757c0,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.Extensions.Configuration.Binder,10.0.0-rc.2.25502.107,80f04da6beef001d3c357584485c2ddc6fdbf3776cfd10f0d7b40dfe8a79ee43,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.Extensions.Configuration.CommandLine,10.0.0-rc.2.25502.107,91974a95ae35bcfcd5e977427f3d0e6d3416e78678a159f5ec9e55f33a2e19af,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.Extensions.Configuration.EnvironmentVariables,10.0.0-rc.2.25502.107,74d65a20e2764d5f42863f5f203b216533fc51b22fb02a8491036feb98ae5fef,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.Extensions.Configuration.FileExtensions,10.0.0-rc.2.25502.107,5f97b56ea2ba3a1b252022504060351ce457f78ac9055d5fdd1311678721c1a1,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.Extensions.Configuration.Json,10.0.0-rc.2.25502.107,0ba362c479213eb3425f8e14d8a8495250dbaf2d5dad7c0a4ca8d3239b03c392,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.Extensions.DependencyInjection,10.0.0-rc.2.25502.107,2e1b51b4fa196f0819adf69a15ad8c3432b64c3b196f2ed3d14b65136a6a8709,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.Extensions.DependencyInjection.Abstractions,10.0.0-rc.2.25502.107,d6787ccf69e09428b3424974896c09fdabb8040bae06ed318212871817933352,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.Extensions.Diagnostics.Abstractions,10.0.0-rc.2.25502.107,b4bc47b4b4ded4ab2f134d318179537cbe16aed511bb3672553ea197929dc7d8,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.Extensions.Diagnostics.HealthChecks,10.0.0-rc.2.25502.107,855fd4da26b955b6b1d036390b1af10564986067b5cc6356cffa081c83eec158,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions,10.0.0-rc.2.25502.107,59f4724daed68a067a661e208f0a934f253b91ec5d52310d008e185bc2c9294c,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.Extensions.Hosting,10.0.0-rc.2.25502.107,ea9b1fa8e50acae720294671e6c36d4c58e20cfc9720335ab4f5ad4eba92cf62,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.Extensions.Hosting.Abstractions,10.0.0-rc.2.25502.107,98fa23ac82e19be221a598fc6f4b469e8b00c4ca2b7a42ad0bfea8b63bbaa9a2,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.Extensions.Http,10.0.0-rc.2.25502.107,c63c8bf4ca637137a561ca487b674859c2408918c4838a871bb26eb0c809a665,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.Extensions.Http.Polly,10.0.0-rc.2.25502.107,0b436196bcedd484796795f6a795d7a191294f1190f7a477f1a4937ef7f78110,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.Extensions.Logging.Abstractions,10.0.0-rc.2.25502.107,92b9a5ed62fe945ee88983af43c347429ec15691c9acb207872c548241cef961,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.Extensions.Logging.Console,10.0.0-rc.2.25502.107,fa1e10b5d6261675d9d2e97b9584ff9aaea2a2276eac584dfa77a1e35dcc58f5,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.Extensions.Options,10.0.0-rc.2.25502.107,d208acec60bec3350989694fd443e2d2f0ab583ad5f2c53a2879ade16908e5b4,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.Extensions.Options.ConfigurationExtensions,10.0.0-rc.2.25502.107,c2863bb28c36fd67f308dd4af486897b512d62ecff2d96613ef954f5bef443e2,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.Extensions.TimeProvider.Testing,9.10.0,919a47156fc13f756202702cacc6e853123c84f1b696970445d89f16dfa45829,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.IdentityModel.Tokens,8.14.0,00b78c7b7023132e1d6b31d305e47524732dce6faca92dd16eb8d05a835bba7a,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
Microsoft.SourceLink.GitLab,8.0.0,a7efb9c177888f952ea8c88bc5714fc83c64af32b70fb080a1323b8d32233973,https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/flat2
|
||||
|
99
devops/docs/ops-devops-readme.md
Normal file
99
devops/docs/ops-devops-readme.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# DevOps Release Automation
|
||||
|
||||
The **release** workflow builds and signs the StellaOps service containers,
|
||||
generates SBOM + provenance attestations, and emits a canonical
|
||||
`release.yaml`. The logic lives under `ops/devops/release/` and is invoked
|
||||
by the new `.gitea/workflows/release.yml` pipeline.
|
||||
|
||||
## Local dry run
|
||||
|
||||
```bash
|
||||
./ops/devops/release/build_release.py \
|
||||
--version 2025.10.0-edge \
|
||||
--channel edge \
|
||||
--dry-run
|
||||
```
|
||||
|
||||
Outputs land under `out/release/`. Use `--no-push` to run full builds without
|
||||
pushing to the registry.
|
||||
|
||||
After the build completes, run the verifier to validate recorded hashes and artefact
|
||||
presence:
|
||||
|
||||
```bash
|
||||
python ops/devops/release/verify_release.py --release-dir out/release
|
||||
```
|
||||
|
||||
## Python analyzer smoke & signing
|
||||
|
||||
`dotnet run --project src/Tools/LanguageAnalyzerSmoke` exercises the Python language
|
||||
analyzer plug-in against the golden fixtures (cold/warm timings, determinism). The
|
||||
release workflow runs this harness automatically and then produces Cosign
|
||||
signatures + SHA-256 sidecars for `StellaOps.Scanner.Analyzers.Lang.Python.dll`
|
||||
and its `manifest.json`. Keep `COSIGN_KEY_REF`/`COSIGN_IDENTITY_TOKEN` populated so
|
||||
the step can sign the artefacts; the generated `.sig`/`.sha256` files ship with the
|
||||
Offline Kit bundle.
|
||||
|
||||
## Required tooling
|
||||
|
||||
- Docker 25+ with Buildx
|
||||
- .NET 10 preview SDK (builds container stages and the SBOM generator)
|
||||
- Node.js 20 (Angular UI build)
|
||||
- Helm 3.16+
|
||||
- Cosign 2.2+
|
||||
|
||||
Supply signing material via environment variables:
|
||||
|
||||
- `COSIGN_KEY_REF` – e.g. `file:./keys/cosign.key` or `azurekms://…`
|
||||
- `COSIGN_PASSWORD` – password protecting the above key
|
||||
|
||||
The workflow defaults to multi-arch (`linux/amd64,linux/arm64`), SBOM in
|
||||
CycloneDX, and SLSA provenance (`https://slsa.dev/provenance/v1`).
|
||||
|
||||
## Debug store extraction
|
||||
|
||||
`build_release.py` now exports stripped debug artefacts for every ELF discovered in the published images. The files land under `out/release/debug/.build-id/<aa>/<rest>.debug`, with metadata captured in `debug/debug-manifest.json` (and a `.sha256` sidecar). Use `jq` to inspect the manifest or `readelf -n` to spot-check a build-id. Offline Kit packaging should reuse the `debug/` directory as-is.
|
||||
|
||||
## UI auth smoke (Playwright)
|
||||
|
||||
As part of **DEVOPS-UI-13-006** the pipelines will execute the UI auth smoke
|
||||
tests (`npm run test:e2e`) after building the Angular bundle. See
|
||||
`docs/modules/ui/operations/auth-smoke.md` for the job design, environment stubs, and
|
||||
offline runner considerations.
|
||||
|
||||
## NuGet preview bootstrap
|
||||
|
||||
`.NET 10` preview packages (Microsoft.Extensions.*, JwtBearer 10.0 RC, Sqlite 9 RC)
|
||||
ship from the public `dotnet-public` Azure DevOps feed. We mirror them into
|
||||
`./local-nuget` so restores succeed inside Offline Kit.
|
||||
|
||||
1. Run `./ops/devops/sync-preview-nuget.sh` whenever you update the manifest.
|
||||
2. The script now understands the optional `SourceBase` column (V3 flat container)
|
||||
and writes packages alongside their SHA-256 checks.
|
||||
3. `NuGet.config` registers the mirror (`local`), dotnet-public, and nuget.org.
|
||||
|
||||
Use `python3 ops/devops/validate_restore_sources.py` to prove the repo still
|
||||
prefers the local mirror and that `Directory.Build.props` enforces the same order.
|
||||
The validator now runs automatically in the `build-test-deploy` and `release`
|
||||
workflows so CI fails fast when a feed priority regression slips in.
|
||||
|
||||
Detailed operator instructions live in `docs/modules/devops/runbooks/nuget-preview-bootstrap.md`.
|
||||
|
||||
## CI harnesses (offline-friendly)
|
||||
|
||||
- **Concelier**: `ops/devops/concelier-ci-runner/run-concelier-ci.sh` builds `concelier-webservice.slnf` and runs WebService + Storage Mongo tests. Outputs binlog + TRX + summary under `ops/devops/artifacts/concelier-ci/<ts>/`.
|
||||
- **Advisory AI**: `ops/devops/advisoryai-ci-runner/run-advisoryai-ci.sh` builds `src/AdvisoryAI/StellaOps.AdvisoryAI.sln`, runs `StellaOps.AdvisoryAI.Tests`, and emits binlog + TRX + summary under `ops/devops/artifacts/advisoryai-ci/<ts>/`. For offline parity, configure a local NuGet feed in `nuget.config`.
|
||||
- **Scanner**: `ops/devops/scanner-ci-runner/run-scanner-ci.sh` builds `src/Scanner/StellaOps.Scanner.sln` and runs core/analyzer/web/worker test buckets with binlog + TRX outputs under `ops/devops/artifacts/scanner-ci/<ts>/`.
|
||||
|
||||
## Telemetry collector tooling (DEVOPS-OBS-50-001)
|
||||
|
||||
- `ops/devops/telemetry/generate_dev_tls.sh` – generates a development CA and
|
||||
client/server certificates for the OpenTelemetry collector overlay (mutual TLS).
|
||||
- `ops/devops/telemetry/smoke_otel_collector.py` – sends OTLP traces/metrics/logs
|
||||
over TLS and validates that the collector increments its receiver counters.
|
||||
- `ops/devops/telemetry/package_offline_bundle.py` – re-packages collector assets for the Offline Kit.
|
||||
- `ops/devops/telemetry/tenant_isolation_smoke.py` – verifies Tempo/Loki tenant isolation with mTLS and scoped headers.
|
||||
- `deploy/compose/docker-compose.telemetry-storage.yaml` – Prometheus/Tempo/Loki stack for staging validation.
|
||||
|
||||
Combine these helpers with `deploy/compose/docker-compose.telemetry.yaml` to run
|
||||
a secured collector locally before rolling out the Helm-based deployment.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user