Skip to content

Commit 887ae47

Browse files
author
Neo
committed
feat: production hardening — SQLite, monitoring, backups, Docker, CI
Replaces the file-backed JSON memory with a single SQLite database (WAL, async wrapper) and routes wallet sessions, nonces, agent KV, and conversation turns through it. Adds an in-process MetricsCollector and optional Sentry init, a daily online-backup loop with retention pruning, and a multi-stage Dockerfile + docker-compose for single-host deploys. CI gains Slither static analysis for the contracts, a pdoc workflow that publishes the Python API reference to GitHub Pages, and a tag-driven changelog generator that opens a PR. The oracle gateway grows a ``request_safe`` path that falls back to stale-cache entries when the upstream provider is unreachable, and the service dispatcher now tags errors with a category (validation / not_found / service_unavailable / service_error / not_implemented) and a ``degraded`` flag so callers can distinguish hard failures from transient ones. 180 tests pass.
1 parent 83dc545 commit 887ae47

25 files changed

Lines changed: 2187 additions & 187 deletions

.dockerignore

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# VCS
2+
.git
3+
.gitignore
4+
.gitattributes
5+
6+
# Editor / OS junk
7+
.vscode
8+
.idea
9+
.DS_Store
10+
*.swp
11+
12+
# Python build artefacts
13+
__pycache__
14+
*.pyc
15+
*.pyo
16+
*.pyd
17+
*.egg-info
18+
.eggs
19+
build
20+
dist
21+
.pytest_cache
22+
.mypy_cache
23+
.ruff_cache
24+
htmlcov
25+
.coverage
26+
.coverage.*
27+
*.log
28+
29+
# Local virtualenvs
30+
.venv
31+
venv
32+
env
33+
34+
# Node / SDK builds (not needed inside the gateway image)
35+
node_modules
36+
37+
# Local persistent state — must come from a mounted volume, not baked in
38+
data
39+
memory
40+
backups
41+
*.db
42+
*.db-wal
43+
*.db-shm
44+
45+
# Secrets
46+
.env
47+
.env.*
48+
openmatrix.config.json
49+
50+
# Docs and CI artefacts
51+
docs/_build
52+
site
53+
.github
54+
55+
# Tests are useful in CI but not needed inside the runtime image
56+
tests

.github/workflows/changelog.yml

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
name: Changelog
2+
3+
# When a new ``v*`` tag is pushed, regenerate the changelog from commits
4+
# since the previous tag and open a pull request that bumps
5+
# CHANGELOG.md.
6+
7+
on:
8+
push:
9+
tags:
10+
- 'v*'
11+
workflow_dispatch:
12+
inputs:
13+
version:
14+
description: 'Version label for the changelog section'
15+
required: true
16+
default: 'UNRELEASED'
17+
18+
permissions:
19+
contents: write
20+
pull-requests: write
21+
22+
jobs:
23+
generate:
24+
runs-on: ubuntu-latest
25+
steps:
26+
- uses: actions/checkout@v4
27+
with:
28+
fetch-depth: 0 # full history so we can diff against previous tag
29+
30+
- name: Set up Python
31+
uses: actions/setup-python@v5
32+
with:
33+
python-version: '3.11'
34+
35+
- name: Determine version label
36+
id: version
37+
run: |
38+
if [[ -n "${{ github.event.inputs.version }}" ]]; then
39+
echo "label=${{ github.event.inputs.version }}" >> "$GITHUB_OUTPUT"
40+
else
41+
# Strip the leading v from the tag (v1.2.3 -> 1.2.3)
42+
echo "label=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
43+
fi
44+
45+
- name: Determine previous tag
46+
id: prev
47+
run: |
48+
# Most recent v* tag *before* the one that triggered this run.
49+
PREV=$(git tag --list 'v*' --sort=-creatordate \
50+
| grep -v "^${GITHUB_REF_NAME}$" \
51+
| head -n 1 || true)
52+
echo "tag=${PREV}" >> "$GITHUB_OUTPUT"
53+
54+
- name: Regenerate CHANGELOG.md
55+
run: |
56+
if [[ -n "${{ steps.prev.outputs.tag }}" ]]; then
57+
python scripts/generate_changelog.py \
58+
--since "${{ steps.prev.outputs.tag }}" \
59+
--version "${{ steps.version.outputs.label }}" \
60+
--write
61+
else
62+
python scripts/generate_changelog.py \
63+
--version "${{ steps.version.outputs.label }}" \
64+
--write
65+
fi
66+
67+
- name: Open pull request
68+
uses: peter-evans/create-pull-request@v6
69+
with:
70+
commit-message: "docs(changelog): bump for ${{ steps.version.outputs.label }}"
71+
title: "docs(changelog): ${{ steps.version.outputs.label }}"
72+
body: |
73+
Auto-generated changelog for **${{ steps.version.outputs.label }}**.
74+
75+
Generated from commits since `${{ steps.prev.outputs.tag }}` using
76+
`scripts/generate_changelog.py`. Review the categorisation before
77+
merging — anything in the `Other` bucket may need a manual touch-up.
78+
branch: chore/changelog-${{ steps.version.outputs.label }}
79+
base: main
80+
delete-branch: true
81+
author: "Neo <neo@openmatrix.io>"
82+
committer: "Neo <neo@openmatrix.io>"

.github/workflows/docs.yml

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
name: API Docs
2+
3+
# Generate the Python API reference with pdoc and publish it to GitHub
4+
# Pages on every push to main. PR builds run pdoc as a smoke test (to
5+
# catch broken docstrings) but skip the deploy step.
6+
7+
on:
8+
push:
9+
branches: [main]
10+
paths:
11+
- 'runtime/**/*.py'
12+
- 'gateway/**/*.py'
13+
- 'hivemind/**/*.py'
14+
- '.github/workflows/docs.yml'
15+
pull_request:
16+
branches: [main]
17+
paths:
18+
- 'runtime/**/*.py'
19+
- 'gateway/**/*.py'
20+
- 'hivemind/**/*.py'
21+
- '.github/workflows/docs.yml'
22+
workflow_dispatch:
23+
24+
permissions:
25+
contents: read
26+
pages: write
27+
id-token: write
28+
29+
concurrency:
30+
group: pages
31+
cancel-in-progress: true
32+
33+
jobs:
34+
build:
35+
name: Build pdoc site
36+
runs-on: ubuntu-latest
37+
steps:
38+
- uses: actions/checkout@v4
39+
40+
- name: Set up Python
41+
uses: actions/setup-python@v5
42+
with:
43+
python-version: '3.11'
44+
cache: pip
45+
46+
- name: Install dependencies
47+
run: |
48+
python -m pip install --upgrade pip
49+
pip install -r requirements.txt
50+
pip install pdoc
51+
52+
- name: Build API docs
53+
run: |
54+
mkdir -p site
55+
pdoc \
56+
--output-directory site \
57+
--docformat google \
58+
--logo "https://raw.githubusercontent.com/${{ github.repository }}/main/docs/logo.png" \
59+
--footer-text "0pnMatrx — built by Neo" \
60+
runtime gateway hivemind
61+
62+
- name: Upload Pages artifact
63+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
64+
uses: actions/upload-pages-artifact@v3
65+
with:
66+
path: site
67+
68+
deploy:
69+
name: Deploy to GitHub Pages
70+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
71+
needs: build
72+
runs-on: ubuntu-latest
73+
environment:
74+
name: github-pages
75+
url: ${{ steps.deployment.outputs.page_url }}
76+
steps:
77+
- id: deployment
78+
uses: actions/deploy-pages@v4

.github/workflows/slither.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: Slither
2+
3+
# Static analysis for the Solidity contracts under contracts/.
4+
#
5+
# Runs on every push and PR that touches a .sol file. Findings of severity
6+
# "high" fail the build; lower severities are uploaded as a SARIF report
7+
# so they show up in the repository's Security tab.
8+
9+
on:
10+
push:
11+
branches: [main]
12+
paths:
13+
- 'contracts/**/*.sol'
14+
- '.github/workflows/slither.yml'
15+
pull_request:
16+
branches: [main]
17+
paths:
18+
- 'contracts/**/*.sol'
19+
- '.github/workflows/slither.yml'
20+
workflow_dispatch:
21+
22+
permissions:
23+
contents: read
24+
security-events: write
25+
26+
jobs:
27+
slither:
28+
name: Slither static analysis
29+
runs-on: ubuntu-latest
30+
steps:
31+
- name: Checkout
32+
uses: actions/checkout@v4
33+
34+
- name: Run Slither
35+
id: slither
36+
uses: crytic/slither-action@v0.4.0
37+
with:
38+
target: 'contracts/'
39+
slither-version: '0.10.4'
40+
solc-version: '0.8.24'
41+
fail-on: 'high'
42+
sarif: results.sarif
43+
44+
- name: Upload SARIF report
45+
if: always() && steps.slither.outputs.sarif != ''
46+
uses: github/codeql-action/upload-sarif@v3
47+
with:
48+
sarif_file: ${{ steps.slither.outputs.sarif }}
49+
category: slither

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ openmatrix.config.json
1313
# Memory data (agent conversation/kv storage at runtime)
1414
/memory/
1515

16+
# SQLite database, WAL/SHM files, and daily backups
17+
/data/
18+
*.db
19+
*.db-wal
20+
*.db-shm
21+
22+
# Generated API docs site
23+
/site/
24+
1625
# Demo artifacts
1726
demo_deployment.json
1827

Dockerfile

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# syntax=docker/dockerfile:1.6
2+
#
3+
# 0pnMatrx gateway image.
4+
#
5+
# Multi-stage build:
6+
# 1. ``builder`` installs Python dependencies into a virtualenv. Build
7+
# tools (gcc, etc.) live only in this stage so they don't bloat the
8+
# runtime image.
9+
# 2. ``runtime`` copies the prebuilt venv plus application code, runs
10+
# as a non-root user, and exposes the gateway HTTP port.
11+
12+
# ── Stage 1: builder ──────────────────────────────────────────────────────────
13+
FROM python:3.11-slim AS builder
14+
15+
ENV PYTHONDONTWRITEBYTECODE=1 \
16+
PYTHONUNBUFFERED=1 \
17+
PIP_NO_CACHE_DIR=1 \
18+
PIP_DISABLE_PIP_VERSION_CHECK=1
19+
20+
# Build deps for native wheels (eth-hash, pynacl, etc.).
21+
RUN apt-get update && apt-get install -y --no-install-recommends \
22+
build-essential \
23+
gcc \
24+
libssl-dev \
25+
libffi-dev \
26+
pkg-config \
27+
&& rm -rf /var/lib/apt/lists/*
28+
29+
WORKDIR /build
30+
COPY requirements.txt ./
31+
RUN python -m venv /opt/venv \
32+
&& /opt/venv/bin/pip install --upgrade pip setuptools wheel \
33+
&& /opt/venv/bin/pip install -r requirements.txt
34+
35+
# ── Stage 2: runtime ──────────────────────────────────────────────────────────
36+
FROM python:3.11-slim AS runtime
37+
38+
ENV PYTHONDONTWRITEBYTECODE=1 \
39+
PYTHONUNBUFFERED=1 \
40+
PATH="/opt/venv/bin:$PATH" \
41+
OPNMATRX_HOME=/app
42+
43+
# Minimal runtime libs (sqlite3 is part of stdlib but the C library is needed
44+
# at runtime; tini gives us a proper PID 1 for clean shutdown signals).
45+
RUN apt-get update && apt-get install -y --no-install-recommends \
46+
libssl3 \
47+
libffi8 \
48+
libsqlite3-0 \
49+
tini \
50+
&& rm -rf /var/lib/apt/lists/*
51+
52+
# Non-root user
53+
RUN groupadd --system --gid 1000 opnmatrx \
54+
&& useradd --system --uid 1000 --gid opnmatrx --home /app --shell /sbin/nologin opnmatrx
55+
56+
COPY --from=builder /opt/venv /opt/venv
57+
58+
WORKDIR /app
59+
COPY --chown=opnmatrx:opnmatrx . /app
60+
61+
# Persistent state lives under /app/data — mount this as a volume.
62+
RUN mkdir -p /app/data /app/data/backups \
63+
&& chown -R opnmatrx:opnmatrx /app/data
64+
65+
USER opnmatrx
66+
EXPOSE 18790
67+
68+
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
69+
CMD python -c "import urllib.request,sys; \
70+
sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:18790/health', timeout=3).status==200 else 1)" \
71+
|| exit 1
72+
73+
ENTRYPOINT ["/usr/bin/tini", "--"]
74+
CMD ["python", "-m", "gateway.server"]

0 commit comments

Comments
 (0)