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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
16 changes: 8 additions & 8 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,37 +1,37 @@
# No enviar al contexto de build lo que se regenera o no hace falta en la imagen.
# Keep out of the build context anything that is regenerated or not needed in the image.
node_modules
.next
.git
.gitignore

# Secretos: NUNCA deben acabar en la imagen (se inyectan por entorno en runtime).
# Secrets: must NEVER end up in the image (they are injected via the environment at runtime).
.env
.env.*
!.env.example

# Documentación y herramientas de desarrollo.
# Documentation and development tooling.
docs
*.md
.claude
.agents
skills-lock.json

# Datos locales: copias de seguridad (volcados con datos personales), adjuntos de
# desarrollo y claves. `COPY . .` los metería en el builder y en su caché. `**/` cubre
# también los subdirectorios.
# Local data: backups (dumps with personal data), development attachments and keys.
# `COPY . .` would put them in the builder and its cache. `**/` also covers
# subdirectories.
backups
**/*.sql.gz
**/*.tar.gz
uploads
data
**/*.pem

# Lo que la imagen no usa: tests, cobertura y scripts que se ejecutan en el host.
# Things the image does not use: tests, coverage and scripts that run on the host.
coverage
test
scripts
install.sh

# Logs y temporales
# Logs and temporary files
npm-debug.log*
*.tsbuildinfo
69 changes: 40 additions & 29 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,51 +1,62 @@
# Plantilla de variables de entorno de orgtable.
# En una instalación real, install.sh genera un .env con secretos aleatorios.
# Para desarrollo local, copia este archivo a .env y ajusta los valores.
# orgtable environment variable template.
# In a real installation, install.sh generates a .env with random secrets.
# For local development, copy this file to .env and adjust the values.

# --- PostgreSQL --------------------------------------------------------------
POSTGRES_USER=orgtable
POSTGRES_PASSWORD=changeme
POSTGRES_DB=orgtable

# URL de conexión que usa la app. En docker-compose el host es "postgres" (nombre
# del servicio). En desarrollo local suele ser localhost con el puerto mapeado.
# Connection URL used by the app. In docker-compose the host is "postgres" (the service
# name). In local development it is usually localhost with the mapped port.
DATABASE_URL=postgresql://orgtable:changeme@postgres:5432/orgtable

# --- Aplicación --------------------------------------------------------------
# Secreto para firmar las sesiones JWT. OBLIGATORIO, mínimo 16 caracteres: sin él (o
# con uno más corto) la app arranca, pero no puede crear sesiones y el login y el
# asistente inicial fallan. Genera uno con: openssl rand -hex 32
# --- Application -------------------------------------------------------------
# Secret used to sign the JWT sessions. REQUIRED, at least 16 characters: without it (or
# with a shorter one) the app starts, but it cannot create sessions and both login and
# the setup wizard fail. Generate one with: openssl rand -hex 32
SESSION_SECRET=

# Puerto expuesto en el host. Admite también «127.0.0.1:3000» para publicar la app solo
# en local, detrás de un proxy inverso (ver docs/self-hosting.md).
# Port exposed on the host. Also accepts "127.0.0.1:3000" to publish the app on
# localhost only, behind a reverse proxy (see docs/self-hosting.md).
APP_PORT=3000

# Zona horaria de las fechas que formatea el servidor (IANA). Por defecto Atlantic/Canary.
# Time zone (IANA) for dates formatted by the server. Defaults to Atlantic/Canary.
# ORGTABLE_TIMEZONE=Atlantic/Canary

# Número de proxies de confianza delante de la app (entero >= 0). Con 0 (por defecto) se
# ignora X-Forwarded-For y el límite de intentos de login va solo por email. Con N > 0
# se toma la IP N-ésima de X-Forwarded-For contando por la derecha (1 = un solo proxy
# inverso, p. ej. Caddy o nginx). Ver docs/self-hosting.md.
# Number of trusted proxies in front of the app (integer >= 0). With 0 (the default)
# X-Forwarded-For is ignored and the login attempt limit is per email only. With N > 0
# the N-th IP of X-Forwarded-For counting from the right is used (1 = a single reverse
# proxy, e.g. Caddy or nginx). See docs/self-hosting.md.
# TRUSTED_PROXY_HOPS=0

# Directorio donde se guardan los adjuntos de las tarjetas. En docker-compose es un
# volumen persistente montado en /data/uploads (valor por defecto). En desarrollo
# local, si no se define, se usa ./uploads. Ver docs/attachments.md.
# Directory where card attachments are stored. In docker-compose it is a persistent
# volume mounted at /data/uploads (the default). In local development, if unset,
# ./uploads is used. See docs/attachments.md.
# ORGTABLE_UPLOAD_DIR=/data/uploads

# --- Tiempo real (opcional) --------------------------------------------------
# Sincronización en vivo entre usuarios. Por defecto funciona en proceso (un único
# contenedor de app), sin configuración. Si despliegas VARIAS réplicas de la app tras
# un balanceador, pon esto a 1 para propagar los eventos entre instancias mediante
# PostgreSQL LISTEN/NOTIFY (no hace falta Redis). Ver docs/realtime.md.
# --- Real time (optional) ----------------------------------------------------
# Live sync between users. By default it works in-process (a single app container),
# with no configuration. If you deploy SEVERAL app replicas behind a load balancer, set
# this to 1 to propagate events between instances via PostgreSQL LISTEN/NOTIFY (no
# Redis needed). See docs/realtime.md.
# ORGTABLE_REALTIME_PG_NOTIFY=0

# --- Recordatorios de vencimiento (opcional) ---------------------------------
# Si defines un secreto, se habilita el endpoint POST /api/cron/reminders, que genera
# notificaciones de tarjetas que vencen pronto o están vencidas. Lo debe disparar un
# programador EXTERNO (cron del sistema, etc.) enviando el secreto como Bearer:
# --- Due-date reminders (optional) -------------------------------------------
# Setting a secret enables the POST /api/cron/reminders endpoint, which creates
# notifications for cards that are due soon or overdue. It must be triggered by an
# EXTERNAL scheduler (system cron, etc.) sending the secret as a Bearer token:
# curl -X POST -H "Authorization: Bearer $CRON_SECRET" http://localhost:3000/api/cron/reminders
# install.sh lo genera solo; a mano: openssl rand -hex 32. Ver docs/reminders.md.
# install.sh generates it for you; by hand: openssl rand -hex 32. See docs/reminders.md.
# CRON_SECRET=

# --- Privacy and open source (optional) --------------------------------------
# Days the audit log is kept (it includes IPs if TRUSTED_PROXY_HOPS > 0). It is purged
# by POST /api/cron/maintenance, with the same CRON_SECRET; schedule it once a day.
# Integer >= 1. See docs/privacy.md.
# ORGTABLE_AUDIT_RETENTION_DAYS=90

# "Source code" link shown by the app. The AGPL (section 13) requires offering the source
# to whoever uses the app over a network: if you deploy a MODIFIED version, point this
# at your fork. Empty = the original repository.
# ORGTABLE_SOURCE_URL=https://github.com/Aimarmndz/orgtable
51 changes: 30 additions & 21 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
branches: [main]
pull_request:

# Si llegan pushes seguidos al mismo ref, cancela la ejecución anterior.
# If several pushes arrive for the same ref, cancel the previous run.
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
Expand All @@ -14,8 +14,8 @@ jobs:
build-and-test:
runs-on: ubuntu-latest

# PostgreSQL efímero para los tests de integración. La base de datos se llama
# `orgtable_test` (el guardia de los tests exige que acabe en `_test`).
# Ephemeral PostgreSQL for the integration tests. The database is called
# `orgtable_test` (the test guard requires the name to end in `_test`).
services:
postgres:
image: postgres:17
Expand All @@ -32,41 +32,50 @@ jobs:
--health-retries 10

env:
# Apunta al Postgres de servicio. El build no se conecta (rutas dinámicas); los
# tests de integración sí, y `prisma migrate deploy` crea el esquema ahí.
# Points at the service Postgres. The build does not connect (dynamic routes); the
# integration tests do, and `prisma migrate deploy` creates the schema there.
DATABASE_URL: postgresql://orgtable:orgtable@localhost:5432/orgtable_test

steps:
- uses: actions/checkout@v5

# shellcheck viene instalado en ubuntu-latest. Los scripts de operación tocan datos
# de producción (backups, restauración), así que un error de shell no puede pasar.
# shellcheck ships with ubuntu-latest. The ops scripts touch production data
# (backups, restore), so a shell error must not slip through.
- name: Shellcheck
run: shellcheck scripts/*.sh install.sh docker-entrypoint.sh

# El contenedor app solo recibe las variables listadas en `environment:`. Si alguna
# se cae del compose, la función que depende de ella deja de funcionar en Docker sin
# ningún error (p. ej. sin CRON_SECRET los recordatorios responden siempre 503).
# The app container only receives the variables listed under `environment:`. If one
# is dropped from the compose file, the feature that depends on it silently stops
# working in Docker (e.g. without CRON_SECRET reminders always answer 503).
- name: Check compose passes env vars to app
run: |
config="$(CRON_SECRET=probe TRUSTED_PROXY_HOPS=7 ORGTABLE_TIMEZONE=Europe/Madrid docker compose config)"
config="$(CRON_SECRET=probe TRUSTED_PROXY_HOPS=7 ORGTABLE_TIMEZONE=Europe/Madrid \
ORGTABLE_AUDIT_RETENTION_DAYS=45 ORGTABLE_SOURCE_URL=https://example.org/fork \
docker compose config)"
grep -q 'CRON_SECRET: probe' <<<"$config"
grep -q 'TRUSTED_PROXY_HOPS: "7"' <<<"$config"
grep -q 'ORGTABLE_TIMEZONE: Europe/Madrid' <<<"$config"

# La licencia vigente es la de LICENSE (ADR 0016). Falla si vuelve a aparecer la
# antigua (AGPL) u «open source» fuera del ADR sustituido, del registro de decisiones y
# del test que comprueba precisamente que no aparecen en la interfaz.
- name: Check license references
grep -q 'ORGTABLE_AUDIT_RETENTION_DAYS: "45"' <<<"$config"
grep -q 'ORGTABLE_SOURCE_URL: https://example.org/fork' <<<"$config"

# The current license is AGPL-3.0-or-later with an attribution term (section 7(b))
# at the end of LICENSE (see docs/DECISIONES.md, 2026-10-14). Fails if package.json
# and LICENSE stop matching, if the attribution term is lost, or if leftovers of
# the proprietary license reappear outside the historical record.
- name: Check license
run: |
if git grep -n -E 'AGPL|[Oo]pen[ -][Ss]ource' -- \
grep -q '"license": "AGPL-3.0-or-later"' package.json
head -n 1 LICENSE | grep -q 'GNU AFFERO GENERAL PUBLIC LICENSE'
grep -q 'ADDITIONAL TERMS UNDER SECTION 7(b) OF THE GNU AGPL' LICENSE
grep -q 'Created by Aimar (github.com/Aimarmndz)' LICENSE
if git grep -n -E 'MARA-KASSA|UNLICENSED|[Tt]odos los derechos reservados' -- \
':!docs/decisions/0005-agpl.md' ':!docs/decisions/0016-licencia-propietaria.md' \
':!docs/DECISIONES.md' ':!.github/workflows/ci.yml' ':!test/metadata.test.ts'; then
echo "Hay referencias a una licencia que no es la vigente (ver LICENSE)." >&2
':!docs/DECISIONES.md' ':!.github/workflows/ci.yml' ':!CLAUDE.md'; then
echo "Leftovers of the previous license remain (the current one is in LICENSE)." >&2
exit 1
fi

# Instala la versión de pnpm fijada en `packageManager` (package.json).
# Installs the pnpm version pinned in `packageManager` (package.json).
- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v5
Expand All @@ -77,7 +86,7 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile

# Falla si hay vulnerabilidades altas o críticas en las dependencias de producción.
# Fails on high or critical vulnerabilities in production dependencies.
- name: Audit dependencies
run: pnpm audit --prod --audit-level high

Expand Down
8 changes: 4 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@
# production
/build

# adjuntos subidos en desarrollo (en producción es un volumen Docker)
# attachments uploaded in development (a Docker volume in production)
/uploads
/data

# backups locales de la base de datos
# local database backups
/backups
*.sql.gz

Expand All @@ -38,14 +38,14 @@ yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files: se ignoran los reales (contienen secretos), pero se versiona la plantilla
# env files: the real ones are ignored (they hold secrets), the template is versioned
.env*
!.env.example

# vercel
.vercel

# herramientas de desarrollo locales (skills de Claude Code, no parte del proyecto)
# local development tooling (Claude Code skills, not part of the project)
.claude/
.agents/
skills-lock.json
Expand Down
48 changes: 48 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Changelog

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
project uses [Semantic Versioning](https://semver.org/). While it is `0.x`, a minor
version may include breaking changes; they are flagged under "Changed". The reasoning
behind each decision is in [`docs/DECISIONES.md`](./docs/DECISIONES.md).

## [0.2.0] — 2026-10-14

### Added

- Optional open sign-up per instance (`/register`), with consent and anti-abuse limits.
Closed by default.
- Operator legal details in *Admin › Instance* and public privacy, terms, cookies and
legal notice pages.
- Redesigned settings: profile, password change, open sessions, appearance, personal
data download and account deletion.
- Scheduled cleanup `POST /api/cron/maintenance` and the
`ORGTABLE_AUDIT_RETENTION_DAYS` variable.
- Account menu in the top bar. Boards show their cards and members.
- Per-instance `robots.txt`, Open Graph and a custom icon.
- **English and Spanish interface**, English by default. The language comes from the
picker (footer and Settings › Appearance), the account's preference or the browser.
Notifications are written in each recipient's language. Translations live in
`locales/<code>/*.json`, and adding a language is documented in `locales/README.md`.
- `pnpm test` checks that every language has the same messages, placeholders and plural
forms as English (`test/i18n.test.ts`).
- README with screenshots and GIFs, a Spanish README (`README.es.md`) and
`docs/privacy.md`.

### Changed

- **License: AGPL-3.0-or-later** with an attribution term (section 7(b)). It replaces the
previous proprietary license.
- Bricolage Grotesque and Figtree typefaces (OFL) instead of Cabinet Grotesk and Satoshi.
- `ORGTABLE_SOURCE_URL` sets the "Source code" link. If you deploy a modified version,
point it to your fork.
- The code, tests, documentation and commit messages are in English. The public REST API
answers in English.
- `next` 16.3.8 and patched `sharp` and `source-map-js` (security advisories).
- The `20261014171630_registration_and_legal` and `20261014175506_user_locale`
migrations are applied automatically on startup.

## [0.1.0]

Boards, lists and cards with drag and drop, multi-user with roles, real time,
attachments, search, calendar, notifications, public API with tokens, card collaboration,
due-date reminders and filters. Details in [`docs/roadmap.md`](./docs/roadmap.md).
Loading
Loading