') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); product: rebuild as durable Sky Notify service by skylerblue333 · Pull Request #1 · skylerblue333/Python-Notification-Router · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 28 additions & 9 deletions .github/workflows/ci.yml
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,31 @@
name: CI
on: [push, pull_request]
name: notify-ci
on:
pull_request:
push:
branches: [main]

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
verify:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: pip install -r requirements.txt
- run: pytest tests/ -v
python-version: '3.12'
- name: Install dependencies
run: python -m pip install --upgrade pip && pip install -r requirements-dev.txt
- name: Compile
run: python -m compileall -q src tests
- name: Lint
run: ruff check src tests

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Make the new Ruff gate pass

In the newly added verify lint step, running the exact documented command ruff check src tests exits with status 1 because tests/test_main.py:9 triggers E712 (== True). Consequently, every CI run stops at this step before tests, auditing, or the image build; fix the retained violation or explicitly configure the intended Ruff rules before making this a required gate.

Useful? React with 👍 / 👎.

- name: Test
run: pytest -q
- name: Audit runtime dependencies
run: pip-audit -r requirements.txt
- name: Build hardened container
run: docker build -t sky-notify:ci .
- name: Verify non-root image declaration
run: test "$(docker image inspect sky-notify:ci --format '{{.Config.User}}')" = "sky"
11 changes: 8 additions & 3 deletions Dockerfile
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
FROM python:3.11-slim
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
RUN groupadd --system sky && useradd --system --gid sky --home-dir /app sky
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
RUN pip install --no-cache-dir --upgrade pip && pip install --no-cache-dir -r requirements.txt
COPY src ./src
RUN mkdir -p /app/data && chown -R sky:sky /app
USER sky
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/healthz', timeout=3)"
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]
13 changes: 13 additions & 0 deletions PRODUCT.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
# Sky Notify product scope

**Product:** Sky Notify

**Purpose:** durable single-node notification routing for applications that need idempotent submission, explicit delivery state, retry budgeting, and auditable provider outcomes.

**Suitable uses:** internal platform notifications, webhook fan-out, local/CI delivery simulation, and service-boundary integration inside SKYCOIN4444.

**Supported deployment:** one service instance with persistent SQLite storage. Horizontal multi-writer clustering is not part of this release.

**Commercial packaging boundary:** this repository can be deployed as a standalone notification microservice, but operators supply infrastructure, TLS, secrets, backup, monitoring, and external provider contracts.

See `README.md` and `SECURITY.md` for verified behavior and limitations.
70 changes: 42 additions & 28 deletions README.md
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,58 @@
<!-- PORTFOLIO PROJECT PROFILE: maintained by the repository owner -->
# Sky Notify

## Project profile and code-audit snapshot
Sky Notify is a focused notification-delivery service for the SKYCOIN4444 engineering ecosystem. It provides durable submission, idempotency, retry/dead-letter state, operational endpoints, and explicit provider delivery results without claiming delivery before the configured provider actually succeeds.

**What this is:** **Python-Notification-Router** is a public repository described as: “Enterprise-grade notification router implementation in Python. #SkyCoin4444 #AI #Blockchain #DevOps #Innovation” Its dominant language signals are **Python (5 files)**.
## What is implemented

**Why it has value:** Its value is best understood through the implementation evidence currently present in the repository: **19 tracked files** were observed in the shallow audit, with the source structure and existing documentation providing the project’s specific context. This README does not treat a prototype, experiment, or archive as a production system without supporting evidence.
- FastAPI HTTP API on Python 3.12
- SQLite-backed durable notification records
- idempotent submissions keyed by caller-provided idempotency keys
- `pending`, `sending`, `delivered`, and `dead_letter` lifecycle states
- bounded retry budgets with exponential delay
- `log` adapter for deterministic local/CI verification
- HTTPS webhook adapter with an explicit hostname allowlist
- redirect refusal for webhook calls
- optional constant-time bearer-token protection
- 64 KiB bounded JSON payloads
- `/healthz`, `/readyz`, and `/metrics`
- non-root container packaging and persistent `/app/data` state
- compile, Ruff, pytest, `pip-audit`, Docker-build, and non-root CI gates

**Implementation evidence:** 2 test-related file(s) detected; 2 dependency or package manifest(s) detected; 2 build/CI/infrastructure signal(s) detected; and 3 documentation or governance file(s) detected. Test filenames observed include `tests/test_main.py`, `tests/test_router.py`. Dependency or package files include `package.json`, `requirements.txt`. Build, CI, or infrastructure signals include `Dockerfile`, `.github/workflows/ci.yml`.
## Run locally

**Current status:** The repository is tracked on the `main` branch. The existing source tree, configuration, tests, workflows, and documentation remain authoritative for supported behavior and maturity. A code audit is not a production-readiness certification, and the presence of a test or workflow file does not establish that all checks pass.
```bash
python -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
uvicorn src.main:app --host 127.0.0.1 --port 8000
```

**Relationship to the wider portfolio:** This repository is one focused component of the broader Skyler Blue Spillers portfolio across AI, software engineering, cloud and DevOps, cybersecurity, blockchain, finance, education, social systems, and creative work. It may provide a service boundary, implementation pattern, experiment, archive, or reusable idea for related repositories. Treat repositories as technical dependencies only where documented interfaces and verified project requirements support that relationship.
To allow webhook delivery, configure exact destination hostnames:

**Quality and security note:** No obvious secret-like pattern was detected by the limited static scan; this is not a substitute for a security audit. No TODO/FIXME marker was detected in the scanned text files.
```bash
export NOTIFY_WEBHOOK_HOSTS="hooks.example.com,events.example.net"
```

---
Optionally protect mutation/read endpoints:

# Python Notification Router
```bash
export NOTIFY_API_TOKEN="replace-with-a-secret-at-least-16-characters"
```

![GitHub stars](https://img.shields.io/github/stars/skylerblue333/Python-Notification-Router?style=flat-square)
![GitHub license](https://img.shields.io/github/license/skylerblue333/Python-Notification-Router?style=flat-square)
## Example submission

## 🌟 Overview
**Python-Notification-Router** is a professional-grade project within the **SkyCoin4444** ecosystem. It focuses on delivering high-value solutions in the domain of **Python**.
```bash
curl -X POST http://127.0.0.1:8000/api/v1/notifications \
-H 'Content-Type: application/json' \
-d '{"channel":"log","destination":"stdout","payload":{"event":"build.complete"},"idempotencyKey":"build-123","maxAttempts":3}'

## 🚀 Key Features
- **Scalable Architecture**: Designed for enterprise-level growth and performance.
- **Modern Standards**: Implements best practices for clean code and maintainability.
- **Robust Integration**: Built to work seamlessly within modern cloud-native environments.
curl -X POST http://127.0.0.1:8000/internal/run-once
```

## 🛠️ Technology Stack
- **Primary Domain**: Python
- **Ecosystem**: SkyCoin4444 Digital Platform
## Deployment boundary

## 📂 Structure
The project is organized into a modular structure to ensure clarity and ease of development.
This release is a **single-node durable notification router**. It does not claim distributed queue semantics, exactly-once external delivery, multi-region failover, email/SMS/push provider integrations, tenant isolation, or external compliance certification. Webhook recipients must be explicitly allowlisted. Production operators remain responsible for TLS termination, network policy, secret management, database backup, monitoring, and provider credentials.

## 👨‍💻 Author
**Skyler Blue Spillers**
*Professional Chess Player & Software Engineer*
## Repository role

---
*Powered by SkyCoin4444*
Sky Notify is product #10 in the standalone-product master plan. It remains independently buildable while exposing a clean notification boundary that can later be integrated into the unified SKYCOIN4444 platform.
23 changes: 23 additions & 0 deletions SECURITY.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
# Security model

Sky Notify treats notification destinations and payloads as untrusted input.

## Controls in this release

- webhook delivery requires HTTPS
- webhook hostname must be explicitly allowlisted through `NOTIFY_WEBHOOK_HOSTS`
- embedded URL credentials are rejected
- redirects are not followed
- JSON payloads are bounded to 64 KiB
- optional bearer authentication uses constant-time comparison
- durable idempotency keys reduce accidental duplicate submissions
- external delivery is marked successful only after a 2xx provider response
- retry budgets transition exhausted deliveries to `dead_letter`
- the container runs as an unprivileged `sky` user
- CI performs compile, lint, tests, dependency audit, and image checks

## Explicit limitations

This service does not provide SSRF-proof IP-range filtering beyond the exact hostname allowlist, tenant isolation, message encryption at rest, distributed consensus, exactly-once external delivery, or compliance certification. Operators should place it behind authenticated network boundaries, manage secrets outside the repository, back up the SQLite database, and monitor dead-letter growth.

Do not include credentials or high-value secrets in notification payloads unless the deployment adds an appropriate encrypted storage and data-handling layer.
1 change: 0 additions & 1 deletion main.py

This file was deleted.

3 changes: 3 additions & 0 deletions pytest.ini
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
[pytest]
pythonpath = .
testpaths = tests
5 changes: 5 additions & 0 deletions requirements-dev.txt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
-r requirements.txt
pytest>=8,<9
pytest-asyncio>=0.24,<1
ruff>=0.6,<1
pip-audit>=2.7,<3
8 changes: 4 additions & 4 deletions requirements.txt
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
fastapi==0.103.1
uvicorn==0.23.2
pytest==7.4.2
httpx==0.24.1
fastapi>=0.115,<1
uvicorn[standard]>=0.30,<1
httpx>=0.27,<1
pydantic>=2.8,<3
1 change: 1 addition & 0 deletions src/__init__.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
"""Sky Notify package."""
Loading
Loading