Public-flip audit: generalize internal hosts/paths + drop Sulkta-internal refs

URLs, mount paths, and LAN host bindings parameterized via env or relative paths
so the repo stands up from a clean clone anywhere. Drop cross-codebase refs
("mirrors clawdforge's pattern"), Sulkta-Coop client/merchant test fixtures,
and audit-changelog scaffolding from comments. README terser, technical content
preserved.
This commit is contained in:
Cobb Hayes 2026-05-27 11:25:47 -07:00
parent 8b1774130b
commit b335405c02
23 changed files with 238 additions and 266 deletions

View file

@ -1,6 +1,5 @@
"""Bearer + IP allowlist authentication.
Mirrors clawdforge's pattern:
- Bearer tokens hashed at rest (SHA-256). No plaintext stored.
- Per-token IP allowlist (CIDR list). NULL means "any RFC1918 + loopback"
via the global LAN allowlist.
@ -9,8 +8,7 @@ Mirrors clawdforge's pattern:
- Loopback always allowed (test client uses 127.0.0.1; FastAPI's
`request.client.host` returns 'testclient' under TestClient and we patch
that in tests).
- Bearer tokens NEVER appear in error messages or log lines. Same hygiene
as clawdforge.
- Bearer tokens NEVER appear in error messages or log lines.
"""
from __future__ import annotations

View file

@ -1,9 +1,9 @@
"""SQLite ledger + migrations.
Why SQLite (not MariaDB like clawdforge): single-process, single-host service,
no need for cross-host replication. The runner is the only writer; every
HTTP worker reads. SQLite in WAL mode handles single-writer-many-readers
cleanly. Trade-off documented in README.
Why SQLite: single-process, single-host service, no cross-host replication
needed. The runner is the only writer; every HTTP worker reads. SQLite in
WAL mode handles single-writer-many-readers cleanly. Trade-off documented
in README.
Why stdlib `sqlite3` + `run_in_executor` (not aiosqlite): one less dependency
and the queries are tiny (fetchone / fetchall). The runner does its own log
@ -13,7 +13,7 @@ Migration system:
- Each entry in MIGRATIONS is (version_id, sql_text). Versions are date-tagged
so they sort lexicographically.
- Apply in order, INSERT OR IGNORE into schema_migrations to handle
multi-worker boot races (mirrors cauldron's pattern).
multi-worker boot races.
- Migrations are append-only; never edit a landed migration, add a new one.
"""
from __future__ import annotations

View file

@ -10,11 +10,11 @@ Design notes:
- Idempotency is enforced by a `digest_runs` table with UNIQUE(date, project_name).
Calling run_once twice for the same date will only send one email per project.
- SMTP send is done via stdlib smtplib (sync) wrapped in run_in_executor so
we don't block the loop while postfix grumbles.
we don't block the loop while the relay grumbles.
- If SMTP isn't configured, the server lifespan logs a warning and skips
scheduler startup. The /digests endpoints still work for dry-run rendering.
- Patch-drafted / auto-patches / bugs.sulkta.com numbers are zero-state
placeholders for v0.1 wave 2C wave 3 / step 9 wires them.
- Patch-drafted / auto-patches / external-tracker numbers are zero-state
placeholders until the patch loop is wired up.
"""
from __future__ import annotations
@ -85,7 +85,7 @@ class SmtpConfig:
def _parse_pr_url(pr_url: str) -> tuple[str, str, int] | None:
"""Pull (owner, repo, number) out of a Gitea-style PR URL.
Accepts URLs like ``http://192.168.0.5:3001/Sulkta-Coop/clawdforge/pulls/42``.
Accepts URLs like ``http://git.example.com/org/repo/pulls/42``.
Returns None if the URL doesn't look right — caller treats that as
"can't determine state, assume open".
"""
@ -222,7 +222,7 @@ def _render_text(
lines.append("")
lines.append("Open follow-ups:")
lines.append(f" - {open_followups} unmerged auto-patches")
lines.append(" - 0 manual review tickets in bugs.sulkta.com")
lines.append(" - 0 manual review tickets in external tracker")
lines.append("")
lines.append(f"Full log: {full_log_url}")
return "\n".join(lines) + "\n"
@ -283,7 +283,7 @@ tr td:first-child {{ font-size: 1.2em; }}
<h3>Open follow-ups</h3>
<ul>
<li>{open_followups} unmerged auto-patches</li>
<li>0 manual review tickets in bugs.sulkta.com</li>
<li>0 manual review tickets in external tracker</li>
</ul>
<p class="foot">Full log: <a href="{full_log_url}">{full_log_url}</a></p>
</body></html>
@ -314,7 +314,7 @@ class DigestScheduler:
time_zone: str = "America/Los_Angeles",
hour: int = 6,
minute: int = 0,
full_log_base_url: str = "http://192.168.0.5:8810/digests",
full_log_base_url: str = "http://localhost:8810/digests",
gitea_pr_state_check=None,
):
self.db = db

View file

@ -1056,16 +1056,21 @@ class Patcher:
) -> bool:
"""Commit the worktree changes to a new branch and push to origin.
Author is forced to ``Kayos <kayos@sulkta.com>``. We pass through
--no-gpg-sign because crafting-table containers don't have signing
keys; commit messages reference the finding id so the PR review
can navigate back to the finding row in the API.
Author defaults to ``crafting-table <crafting-table@localhost>``,
overridable via ``CRAFTING_PATCHER_AUTHOR_NAME`` +
``CRAFTING_PATCHER_AUTHOR_EMAIL``. We pass through --no-gpg-sign
because crafting-table containers don't have signing keys; commit
messages reference the finding id so the PR review can navigate
back to the finding row in the API.
"""
import os
author_name = os.environ.get("CRAFTING_PATCHER_AUTHOR_NAME", "crafting-table")
author_email = os.environ.get("CRAFTING_PATCHER_AUTHOR_EMAIL", "crafting-table@localhost")
env = {
"GIT_AUTHOR_NAME": "Kayos",
"GIT_AUTHOR_EMAIL": "kayos@sulkta.com",
"GIT_COMMITTER_NAME": "Kayos",
"GIT_COMMITTER_EMAIL": "kayos@sulkta.com",
"GIT_AUTHOR_NAME": author_name,
"GIT_AUTHOR_EMAIL": author_email,
"GIT_COMMITTER_NAME": author_name,
"GIT_COMMITTER_EMAIL": author_email,
"PATH": "/usr/local/bin:/usr/bin:/bin",
}
msg = (

View file

@ -6,8 +6,7 @@ Authentication model:
- Tokens are flagged is_admin=1 or 0. Admin can do everything.
- Per-app tokens (is_admin=0) can register projects (becoming the owner)
and only see/touch projects where owner_token matches their name.
- Cross-token project access returns 404 (NOT 403) same existence-leak
guard clawdforge uses for sessions.
- Cross-token project access returns 404 (NOT 403) existence-leak guard.
Endpoints:
- GET /healthz public-ish (still needs LAN IP)