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
10 changes: 10 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.git
.github
.codex
.claude
output
state
tmp
*.db
*.sqlite
*.sqlite3
69 changes: 69 additions & 0 deletions .github/workflows/publish-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Publish Image

on:
push:
branches:
- main
paths:
- .dockerignore
- Dockerfile
- .github/workflows/publish-image.yml
- cmd/siftd/**
- internal/**
- docs/contracts/source-registry.seed.json
- go.mod
- go.sum

jobs:
build-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true

- name: Run Unit Tests
run: go test ./...

- name: Run Clustering Eval Gate
run: go run ./cmd/sift eval clustering --dataset docs/contracts/clustering-eval.v0.json --threshold 0.82 --target-precision 0.90 --min-pairs 100 --format text

- name: Setup Buildx
uses: docker/setup-buildx-action@v3

- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Generate image tag
id: tag
env:
GIT_SHA: ${{ github.sha }}
run: |
SHA7=$(echo "$GIT_SHA" | cut -c1-7)
TS14=$(date -u +%Y%m%d%H%M%S)
echo "tag=dev-${SHA7}-${TS14}" >> "$GITHUB_OUTPUT"

- name: Build and push image
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile
push: true
tags: |
ghcr.io/heurema/sift:${{ steps.tag.outputs.tag }}
ghcr.io/heurema/sift:latest
cache-from: type=gha
cache-to: type=gha,mode=max
35 changes: 35 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
FROM golang:1.25.5-alpine AS builder
WORKDIR /src

RUN apk add --no-cache ca-certificates tzdata

COPY go.mod go.sum ./
RUN go mod download

COPY cmd ./cmd
COPY internal ./internal
COPY docs/contracts/source-registry.seed.json ./docs/contracts/source-registry.seed.json

RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags="-s -w" -o /out/siftd ./cmd/siftd

FROM alpine:3.21

RUN apk add --no-cache ca-certificates tzdata \
&& addgroup -S app \
&& adduser -S app -G app \
&& install -d -o app -g app /app /app/docs/contracts /data/output

WORKDIR /app

COPY --from=builder /out/siftd /app/siftd
COPY --from=builder /src/docs/contracts/source-registry.seed.json /app/docs/contracts/source-registry.seed.json

ENV SIFTD_OUTPUT_DIR=/data/output

USER app

EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=3s --start-period=20s --retries=3 CMD wget -qO- http://127.0.0.1:8080/healthz >/dev/null || exit 1

ENTRYPOINT ["/app/siftd"]
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ Implemented now:
- health and readiness endpoints (`/healthz`, `/readyz`);
- Zitadel-protected read API (`/v1/events`, `/v1/events/{event_id}`, `/v1/digests/{scope}/{window}`);
- authenticated WebSocket stream endpoint (`/v1/ws`) with post-sync update notifications (`event.upserted`, `digest.updated`).
- operator deployment assets for single-node Linux hosting (`ops/systemd/siftd.service`, `ops/env/siftd.env.example`, `docs/runbooks/siftd.md`).
- operator deployment assets for single-node Linux hosting (`ops/systemd/siftd.service`, `ops/env/siftd.env.example`, `docs/runbooks/siftd.md`);
- container build artifact (`Dockerfile`) and GHCR publish path (`ghcr.io/heurema/sift`) with deterministic `dev-<sha7>-<timestamp>` tags from CI.

Not implemented yet:

Expand Down
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

## Runbooks

- [siftd.md](runbooks/siftd.md) — single-node operator runbook for the hosted `siftd` service
- [siftd.md](runbooks/siftd.md) — operator runbook for the hosted `siftd` service, including single-node host deployment and container artifact notes

## Research

Expand Down
15 changes: 15 additions & 0 deletions docs/runbooks/siftd.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,27 @@ Target model:
- one reverse proxy in front of `127.0.0.1:8080` if public HTTPS is required.

This is intentionally a single-node operating model.
The repository also publishes a container image for GitOps and cluster deployment, but
this runbook remains the host-first operating baseline.

## Repository Assets

- systemd unit: `ops/systemd/siftd.service`
- environment template: `ops/env/siftd.env.example`
- binary entrypoint: `cmd/siftd/main.go`
- container image build: `Dockerfile`
- CI image publish workflow: `.github/workflows/publish-image.yml`

## Container Artifact

The repository publishes:

- image: `ghcr.io/heurema/sift`
- mutable convenience tag: `latest`
- rollout tag format: `dev-<sha7>-<timestamp>`

The hosted GitOps path should pin one of the `dev-*` tags. `latest` is for manual
inspection and ad-hoc runs only.

## Required Inputs

Expand Down
2 changes: 2 additions & 0 deletions llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Key themes: crypto news, event clustering, provenance, rights-aware ingestion, a
- [Sift Pro Execution Plan](docs/plans/2026-03-07-sift-pro-execution-plan.md): Narrow hosted implementation path for the first paid service slice, now anchored on Postgres and Zitadel.
- [Sift Pro Slice 1 Blueprint](docs/plans/2026-03-07-sift-pro-slice-1-blueprint.md): File-level extraction plan for the shared runtime that keeps local SQLite separate from the future hosted Postgres path.
- [Siftd Runbook](docs/runbooks/siftd.md): Single-node operator runbook for deploying the hosted service with systemd, Postgres, and Zitadel.
- [Dockerfile](Dockerfile): Multi-stage container image definition for the hosted `siftd` runtime.
- [Publish Image Workflow](.github/workflows/publish-image.yml): GHCR image publication for `ghcr.io/heurema/sift` on `main` when hosted runtime inputs change.
- [CLI Entry](cmd/sift/main.go): Current Go CLI implementation entry point.
- [Server Entry](cmd/siftd/main.go): Hosted server entry point with Postgres-backed scheduler and Zitadel-protected read path.
- [Siftd Env Example](ops/env/siftd.env.example): Example hosted environment contract for systemd deployment.
Expand Down