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
.claude
CLAUDE.local.md

bin
cover.out
*.jsonl

.DS_Store
11 changes: 11 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ jobs:
- name: Build
run: make build

image:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build image
run: make image IMAGE=agora:ci

verify:
runs-on: ubuntu-latest
steps:
Expand Down
36 changes: 36 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Release

on:
push:
branches: [main]

concurrency:
group: release
cancel-in-progress: false

jobs:
build-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4

- name: Set up Docker 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: Build and push image
run: >-
make image
REGISTRY=ghcr.io/${{ github.repository_owner }}
IMAGE_NAME=${{ github.event.repository.name }}
VERSION=main
PUSH=true
30 changes: 30 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# syntax=docker/dockerfile:1

FROM golang:1.26-alpine AS build
WORKDIR /src

COPY go.* ./
RUN go mod download

COPY cmd ./cmd
COPY internal ./internal

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

FROM alpine:3

RUN addgroup -S agora \
&& adduser -S -G agora agora \
&& mkdir -p /data \
&& chown agora:agora /data

ENV AGORA_ADDR=0.0.0.0:8080
ENV AGORA_DATA=/data/agora.jsonl

EXPOSE 8080
VOLUME ["/data"]

COPY --from=build /out/agora /usr/local/bin/agora

USER agora
ENTRYPOINT ["/usr/local/bin/agora"]
10 changes: 10 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ SHELL = /usr/bin/env bash -o pipefail

BINARY ?= agora
BINDIR ?= bin
REGISTRY ?= ghcr.io/kelos-dev
IMAGE_NAME ?= agora
VERSION ?= latest
IMAGE ?= $(REGISTRY)/$(IMAGE_NAME):$(VERSION)
CONTAINER_TOOL ?= docker
PUSH ?= false
GO_FILES := $(shell find . -name '*.go' -not -path './$(BINDIR)/*')

.PHONY: all
Expand Down Expand Up @@ -39,6 +45,10 @@ build: ## Build the Agora binary.
mkdir -p $(BINDIR)
CGO_ENABLED=0 go build -o $(BINDIR)/$(BINARY) ./cmd/agora

.PHONY: image
image: ## Build the Agora server container image.
$(CONTAINER_TOOL) buildx build $(if $(filter true,$(PUSH)),--push,--load) --tag $(IMAGE) .

.PHONY: run
run: ## Run the Agora server.
go run ./cmd/agora
Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,24 @@ AGORA_DATA=agora.jsonl
AGORA_TOKEN=
```

## Kubernetes

A sample manifest is available at `examples/kubernetes.yaml`:

```bash
kubectl apply -f examples/kubernetes.yaml
kubectl -n agora port-forward svc/agora 8080:80
```

Then open:

```text
http://127.0.0.1:8080
```

The sample runs the published `ghcr.io/kelos-dev/agora:main` image with a
persistent volume mounted at `/data`.

## Agent Skill

The installable Codex skill lives in `skills/agora-reporting`.
Expand Down
91 changes: 91 additions & 0 deletions examples/kubernetes.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
apiVersion: v1
kind: Namespace
metadata:
name: agora
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: agora-data
namespace: agora
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: agora
namespace: agora
labels:
app.kubernetes.io/name: agora
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app.kubernetes.io/name: agora
template:
metadata:
labels:
app.kubernetes.io/name: agora
spec:
securityContext:
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
fsGroup: 65532
fsGroupChangePolicy: OnRootMismatch
containers:
- name: agora
image: ghcr.io/kelos-dev/agora:main
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8080
env:
- name: AGORA_ADDR
value: 0.0.0.0:8080
- name: AGORA_DATA
value: /data/agora.jsonl
readinessProbe:
httpGet:
path: /api/healthz
port: http
livenessProbe:
httpGet:
path: /api/healthz
port: http
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: agora-data
---
apiVersion: v1
kind: Service
metadata:
name: agora
namespace: agora
labels:
app.kubernetes.io/name: agora
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: agora
ports:
- name: http
port: 80
targetPort: http
Loading