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
17 changes: 17 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,20 @@ jobs:
run: npm run test
- name: build
run: npm run build

# The Dockerfile ships to users from the release workflow, so it is built
# here on every push and pull request (single arch, nothing published) and
# the resulting image is asked for its version.
image:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Build the image
run: |
docker build \
--build-arg VERSION=0.0.0-ci \
--build-arg COMMIT="$(git rev-parse --short HEAD)" \
--build-arg DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--tag davcloud:ci .
- name: The image runs and reports its version
run: docker run --rm davcloud:ci -version
75 changes: 75 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: release

# Tagging is what publishes: a v* tag on main produces a GitHub release with
# the static binaries for linux/amd64 and linux/arm64 (CGO disabled, version
# stamped) and a multi-arch image at ghcr.io/<owner>/davcloud.
on:
push:
tags: ["v*"]

permissions:
contents: write # create the release and upload the binaries
packages: write # push the image to GHCR

jobs:
binaries:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version-file: go.mod
cache: true
- name: Build the static binaries
run: |
set -euo pipefail
version="${GITHUB_REF_NAME#v}"
ldflags="-s -w -X main.version=${version} -X main.commit=$(git rev-parse --short HEAD) -X main.date=$(date -u +%Y-%m-%dT%H:%M:%SZ)"
mkdir -p dist
for arch in amd64 arm64; do
name="davcloud-linux-${arch}"
CGO_ENABLED=0 GOOS=linux GOARCH="${arch}" \
go build -trimpath -ldflags "${ldflags}" -o "dist/${name}" ./cmd/server
(cd dist && sha256sum "${name}" > "${name}.sha256")
done
./dist/davcloud-linux-amd64 -version
ls -l dist
- name: Publish the release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
gh release create "${GITHUB_REF_NAME}" \
--repo "${GITHUB_REPOSITORY}" \
--title "${GITHUB_REF_NAME}" \
--generate-notes \
dist/davcloud-linux-amd64 \
dist/davcloud-linux-amd64.sha256 \
dist/davcloud-linux-arm64 \
dist/davcloud-linux-arm64.sha256

image:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: docker/setup-qemu-action@v4
- uses: docker/setup-buildx-action@v4
- name: Log in to GHCR
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push the multi-arch image
run: |
set -euo pipefail
image="ghcr.io/$(echo "${GITHUB_REPOSITORY}" | tr '[:upper:]' '[:lower:]')"
version="${GITHUB_REF_NAME#v}"
docker buildx build \
--platform linux/amd64,linux/arm64 \
--build-arg VERSION="${version}" \
--build-arg COMMIT="$(git rev-parse --short HEAD)" \
--build-arg DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--tag "${image}:${GITHUB_REF_NAME}" \
--tag "${image}:latest" \
--push .
20 changes: 16 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,27 @@ RUN npm run build

# ── Backend build ───────────────────────────────────────────────────────────
FROM golang:1.24-alpine AS build
# Version metadata as build args, so the binary inside the image reports the
# tag it was built from instead of "dev".
ARG VERSION=dev
ARG COMMIT=unknown
ARG DATE=unknown
WORKDIR /src
COPY go.mod go.sum* ./
# go.sum is generated by `go mod tidy` on first build; tidy also fetches deps.
RUN go mod tidy
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o /out/davcloud ./cmd/server
RUN CGO_ENABLED=0 go build -trimpath \
-ldflags "-s -w -X main.version=${VERSION} -X main.commit=${COMMIT} -X main.date=${DATE}" \
-o /out/davcloud ./cmd/server

# ── Runtime ─────────────────────────────────────────────────────────────────
FROM alpine:3.21
ARG VERSION=dev
LABEL org.opencontainers.image.title="davcloud" \
org.opencontainers.image.description="CalDAV and CardDAV server over a Radicale-compatible data directory" \
org.opencontainers.image.source="https://github.com/gnacho/davcloud" \
org.opencontainers.image.version="${VERSION}" \
org.opencontainers.image.licenses="AGPL-3.0-only"
RUN apk add --no-cache ca-certificates tzdata
COPY --from=build /out/davcloud /usr/local/bin/davcloud
COPY --from=ui /src/dist /app/ui
Expand Down
21 changes: 18 additions & 3 deletions PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ opencloud-contacts) noten la diferencia.
Estado al 18-Oct-2026: **fases 0-4 completadas** (issues #1-#9 y #21 cerrados;
davcloud es el servidor DAV de producción desde el 17-Oct) y **fase 5 en curso**
(soak en marcha; el **drill de rollback pasó el 18-Oct-2026**, issue #22).
**Nada de Docker**: el despliegue es un binario estático + systemd (ver
`deploy/README.md`). Repo público desde el 18-Oct-2026, licencia AGPL-3.0.
El despliegue propio **no usa Docker**: binario estático + systemd (ver
`deploy/README.md`); la imagen de contenedor existe para quien prefiera ese
camino (fase 6). Repo público desde el 18-Oct-2026, licencia AGPL-3.0.

## Hecho

Expand Down Expand Up @@ -50,16 +51,30 @@ davcloud es el servidor DAV de producción desde el 17-Oct) y **fase 5 en curso*
colección leídas y ninguno de los items borrados reapareciendo; vuelta a
davcloud con los items byte a byte intactos (Radicale solo añade su
`.Radicale.cache/item/`).
- **Endurecimiento** (issues #38 y #39, 18-Oct-2026): los nombres gestionados
por el servidor quedan fuera del alcance de los clientes DAV (404 al leer,
400 al escribir, la misma semántica que Radicale) y hay límites explícitos de
tamaño (petición y objeto, con `max-resource-size` anunciado y timeouts de
HTTP); el tope de expansión de recurrencias deja acotadas las consultas
patológicas (un `calendar-query` `SECONDLY` que antes no terminaba en 25 s
responde 207 en 0,0016 s). Desplegado en producción el mismo día, con el
rechazo verificado en vivo.

## Pendiente (issues)

- **#43 - Fase 6: distribución** (abierta el 18-Oct-2026): binarios
`linux/amd64` y `linux/arm64` por tag e imagen multi-arch en
`ghcr.io/gnacho/davcloud`, para que probarlo no exija toolchain. El
despliegue propio no cambia. Versiones de `0.2` en `0.2` (`v0.2.0`,
`v0.4.0`, ...).

- **#22 - Fase 5: soak + drill de rollback** (soak en curso desde el 17-Oct:
davcloud es el único servidor DAV; el drill ya pasó el 18-Oct-2026, ver
arriba). Queda el uso diario con datos reales y, solo entonces, retirar
Radicale del contenedor.
- **Edición de recurrencias en la web UI**: sigue siendo un TODO documentado
en `web/src/lib/ics.ts`.
- **Fase 6 - paridad extendida (opcional, por necesidad)**: iMIP
- **Fase 7 - paridad extendida (opcional, por necesidad)**: iMIP
(invitaciones por email) y compartición de calendarios (requiere ACLs que
el proxy no proporciona). Radicale tampoco cubre bien ninguno de los dos.

Expand Down
44 changes: 40 additions & 4 deletions README.es.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ detrás de un despliegue de
referencia trae Radicale exactamente así), pero funciona detrás de cualquier
proxy que autentique e inyecte la identidad.

Un binario estático (~7 MB), sin dependencias de runtime, sin base de datos,
sin contenedores: el servidor y el volumen de datos son todo el despliegue.
Un binario estático (~7 MB), sin dependencias de runtime, sin base de datos:
el binario y el volumen de datos son todo el despliegue (también publicamos
una imagen de contenedor, para quien la prefiera).

## Qué hace

Expand Down Expand Up @@ -90,8 +91,43 @@ volumen de datos, layout "multifilesystem" de Radicale

## Cómo instalarlo

Requisitos: Go 1.24+ para compilar. `npm` solo si quieres la web UI. Sin
base de datos ni dependencias de runtime.
Para compilarlo necesitas Go 1.24+ (`npm` solo si quieres la web UI). Sin base
de datos ni dependencias de runtime. Un binario de release o la imagen de
contenedor ya se bastan solos.

**Descargar una release** (las etiquetas van de `0.2` en `0.2`: `v0.2.0`,
`v0.4.0`, ...):

```bash
curl -LO https://github.com/gnacho/davcloud/releases/latest/download/davcloud-linux-amd64
curl -LO https://github.com/gnacho/davcloud/releases/latest/download/davcloud-linux-amd64.sha256
sha256sum -c davcloud-linux-amd64.sha256
chmod +x davcloud-linux-amd64
./davcloud-linux-amd64 -version
```

En máquinas ARM usa `arm64` en lugar de `amd64`.

**Contenedor**:

```bash
mkdir -p davcloud-data
docker run -d --name davcloud \
--user "$(id -u):$(id -g)" \
-p 127.0.0.1:5232:5232 \
-v "$PWD/davcloud-data:/data" \
ghcr.io/gnacho/davcloud:latest \
-addr :5232 -data-dir /data -ui-dir /app/ui -dev-user alice
curl -s -o /dev/null -w '%{http_code}\n' -X PROPFIND -H 'Depth: 0' \
http://127.0.0.1:5232/caldav/
# 207
```

La imagen corre como uid 1000, de ahí `--user` con tus propios ids para que
pueda escribir en el directorio montado. `-dev-user` es el atajo de
desarrollo que se fía de toda petición como ese usuario; en producción la
identidad llega en la cabecera `X-Remote-User` que inyecta el proxy, así que
el puerto no debe ser alcanzable por nadie más.

**Compilar**

Expand Down
43 changes: 39 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ same header a reverse proxy injects. Designed to sit behind an
full-stack reference ships Radicale exactly like this), but it works behind
any proxy that authenticates and injects the identity.

One static binary (~7 MB), no runtime dependencies, no database, no
containers: the server and the data volume are the whole deployment.
One static binary (~7 MB), no runtime dependencies, no database: the binary
and the data volume are the whole deployment (a container image is published
as well, for those who prefer one).

## What it does

Expand Down Expand Up @@ -84,8 +85,42 @@ data volume, Radicale "multifilesystem" layout

## How to install it

Requirements: Go 1.24+ to build. `npm` only if you want the web UI. No
database, no runtime dependencies.
To build it you need Go 1.24+ (plus `npm` for the web UI). No database, no
runtime dependencies. A released binary or the container image is
self-contained.

**Download a release** (tags move in steps of `0.2`: `v0.2.0`, `v0.4.0`, ...):

```bash
curl -LO https://github.com/gnacho/davcloud/releases/latest/download/davcloud-linux-amd64
curl -LO https://github.com/gnacho/davcloud/releases/latest/download/davcloud-linux-amd64.sha256
sha256sum -c davcloud-linux-amd64.sha256
chmod +x davcloud-linux-amd64
./davcloud-linux-amd64 -version
```

Use `arm64` instead of `amd64` on ARM machines.

**Container**:

```bash
mkdir -p davcloud-data
docker run -d --name davcloud \
--user "$(id -u):$(id -g)" \
-p 127.0.0.1:5232:5232 \
-v "$PWD/davcloud-data:/data" \
ghcr.io/gnacho/davcloud:latest \
-addr :5232 -data-dir /data -ui-dir /app/ui -dev-user alice
curl -s -o /dev/null -w '%{http_code}\n' -X PROPFIND -H 'Depth: 0' \
http://127.0.0.1:5232/caldav/
# 207
```

The image runs as uid 1000, hence `--user` with your own ids so that it can
write into the bind-mounted directory. `-dev-user` is the development shortcut
that trusts every request as that user; in production the identity comes from
the `X-Remote-User` header injected by the proxy, so the port must stay
unreachable for anybody else.

**Build**

Expand Down
11 changes: 10 additions & 1 deletion deploy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,16 @@ sha256sum ./radicale-vol-<stamp>.tgz # compara con el sha256 sacado dentro de

## 1. Binario estático

En cualquier máquina con Go (el CT no necesita toolchain):
Sin toolchain en el CT. Puedes descargar el binario de la release en vez de
compilarlo (comprueba el `sha256`, que va en el mismo asset):

```
curl -LO https://github.com/gnacho/davcloud/releases/latest/download/davcloud-linux-amd64
curl -LO https://github.com/gnacho/davcloud/releases/latest/download/davcloud-linux-amd64.sha256
sha256sum -c davcloud-linux-amd64.sha256
```

O compilarlo en cualquier máquina con Go:

```
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath \
Expand Down
Loading