diff --git a/.github/workflows/docker-ghcr.yml b/.github/workflows/docker-ghcr.yml
new file mode 100644
index 00000000..c3564903
--- /dev/null
+++ b/.github/workflows/docker-ghcr.yml
@@ -0,0 +1,69 @@
+# SPDX-FileCopyrightText: 2026 LibreCode contributors
+# SPDX-License-Identifier: MIT
+name: Docker GHCR
+
+on:
+ push:
+ branches:
+ - main
+ tags:
+ - "v*"
+ pull_request:
+ paths:
+ - ".dockerignore"
+ - ".github/workflows/docker-ghcr.yml"
+ - "Cargo.lock"
+ - "Cargo.toml"
+ - "Dockerfile"
+ - "appinfo/info.xml"
+ - "build.rs"
+ - "src/**"
+ workflow_dispatch:
+
+permissions:
+ contents: read
+ packages: write
+
+jobs:
+ docker:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v5
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+
+ - name: Extract Docker metadata
+ id: meta
+ uses: docker/metadata-action@v5
+ with:
+ images: ghcr.io/${{ github.repository }}
+ flavor: |
+ latest=auto
+ tags: |
+ type=ref,event=branch
+ type=ref,event=pr
+ type=semver,pattern={{version}}
+ type=semver,pattern={{major}}.{{minor}}
+ type=sha,prefix=sha-
+ type=raw,value=edge,enable={{is_default_branch}}
+
+ - name: Log in to GHCR
+ if: github.event_name != 'pull_request'
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Build and push Docker image
+ uses: docker/build-push-action@v6
+ with:
+ context: .
+ file: ./Dockerfile
+ push: ${{ github.event_name != 'pull_request' }}
+ tags: ${{ steps.meta.outputs.tags }}
+ labels: ${{ steps.meta.outputs.labels }}
+ cache-from: type=gha
+ cache-to: type=gha,mode=max
diff --git a/.gitignore b/.gitignore
index ed48a19e..c3888ee5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
target
.env
+.env.notify_push
bin
build
composer.phar
diff --git a/Dockerfile b/Dockerfile
index bfb6a03a..e3bfaa84 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -26,4 +26,4 @@ FROM scratch
COPY --from=build /notify_push /
EXPOSE 7867
-CMD ["/notify_push"]
+ENTRYPOINT ["/notify_push"]
diff --git a/README.md b/README.md
index 26704135..b33db570 100644
--- a/README.md
+++ b/README.md
@@ -7,8 +7,15 @@
[](https://api.reuse.software/info/github.com/nextcloud/notify_push)
+Portuguese translation: [README.pt-BR.md](./README.pt-BR.md)
+
Update notifications for nextcloud clients
+## Additional documentation
+
+- Running the container locally and publishing images to GHCR:
+ [`docs/docker-ghcr.md`](./docs/docker-ghcr.md)
+
## About
This app attempts to solve the issue where Nextcloud clients have to periodically check the server if any files have
@@ -286,6 +293,63 @@ Once the nginx configuration is edit you can reload nginx using.
sudo nginx -s reload
```
+#### nginx-proxy
+
+If you're using the [`nginx-proxy`](https://github.com/nginx-proxy/nginx-proxy) Docker project, do not add the
+`location` block manually to the Nextcloud container. In that setup the `server` block is generated by `nginx-proxy`,
+so the simplest approach is to publish `notify_push` on the same host under the `/push/` path.
+
+Example using the generic domain `cloud.example.com`:
+
+```yaml
+services:
+ nextcloud:
+ image: nextcloud:apache
+ environment:
+ VIRTUAL_HOST: cloud.example.com
+ VIRTUAL_PATH: /
+ networks:
+ - nginx-proxy
+ - internal
+
+ notify_push:
+ image: ghcr.io/nextcloud/notify_push:latest
+ environment:
+ PORT: "7867"
+ VIRTUAL_HOST: cloud.example.com
+ VIRTUAL_PATH: /push/
+ VIRTUAL_DEST: /
+ VIRTUAL_PORT: "7867"
+ expose:
+ - "7867"
+ networks:
+ - nginx-proxy
+ - internal
+```
+
+Important details:
+
+- `notify_push` must be attached to the same Docker network as `nginx-proxy`
+- use `VIRTUAL_PATH=/push/` with the trailing slash
+- use `VIRTUAL_DEST=/` so the `/push/` prefix is stripped before forwarding the request
+- with `nginx-proxy`, `expose` is usually enough and you do not need to publish port `7867` on the host
+
+If you prefer injecting a custom config snippet into `nginx-proxy`, create a file at
+`/etc/nginx/vhost.d/cloud.example.com` with the following block:
+
+```nginx
+location ^~ /push/ {
+ proxy_pass http://notify_push:7867/;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "Upgrade";
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+}
+```
+
+Again, both trailing slashes in `/push/` and in `proxy_pass` are required.
+
#### Apache
To use apache as a reverse proxy you first need to enable the proxy modules using
@@ -328,6 +392,57 @@ the push server is listening.
The app will automatically run some tests to verify that the push server is configured correctly.
+### Testing
+
+After finishing the configuration, test it at several levels:
+
+1. Configure the push server URL in Nextcloud:
+
+```bash
+occ app:enable notify_push
+occ notify_push:setup https://cloud.example.com/push
+```
+
+2. Run the self-test:
+
+```bash
+occ notify_push:self-test
+```
+
+3. Verify that the proxy responds correctly:
+
+```bash
+curl -I https://cloud.example.com/push
+curl -I https://cloud.example.com/push/
+```
+
+Expected behavior:
+
+- `/push` returns `301` redirecting to `/push/`
+- `/push/` does not return `502` or another proxy upstream error
+
+4. Generate a notification in Nextcloud and watch the `notify_push` logs. For example, keep a browser session open in
+Nextcloud and create a notification, a Talk message, or a file change. Then verify that the service logs connections
+and events.
+
+With Docker Compose:
+
+```bash
+docker compose logs -f notify_push
+```
+
+If you need more detail while testing, temporarily increase the log level:
+
+```bash
+occ notify_push:log debug
+```
+
+After verification, restore the previous log level:
+
+```bash
+occ notify_push:log --restore
+```
+
### Logging
By default, the push server only logs warnings, you can temporarily change the log level with an occ command
diff --git a/README.pt-BR.md b/README.pt-BR.md
new file mode 100644
index 00000000..814875b6
--- /dev/null
+++ b/README.pt-BR.md
@@ -0,0 +1,506 @@
+
+
+# Client Push
+
+[](https://api.reuse.software/info/github.com/nextcloud/notify_push)
+
+Versao em portugues do README. Versao original em ingles: [README.md](./README.md)
+
+Notificacoes de atualizacao para clientes Nextcloud
+
+## Documentacao adicional
+
+- Executar o container localmente e publicar imagens no GHCR:
+ [`docs/docker-ghcr.md`](./docs/docker-ghcr.md)
+- Implantar o servico em um stack Nextcloud Docker existente:
+ [`deploy/nextcloud/README.md`](./deploy/nextcloud/README.md)
+
+## Sobre
+
+Este app tenta resolver o problema de os clientes Nextcloud precisarem verificar periodicamente o servidor para saber
+se algum arquivo foi alterado. Para manter a sincronizacao responsiva, os clientes tendem a verificar atualizacoes com
+frequencia, o que aumenta a carga no servidor.
+
+Quando muitos clientes fazem essas verificacoes ao mesmo tempo, uma parte consideravel da carga do servidor pode
+consistir apenas nessas checagens.
+
+Ao fornecer uma forma de o servidor enviar notificacoes de atualizacao para os clientes, a necessidade dessas
+checagens pode ser bastante reduzida.
+
+As notificacoes de atualizacao sao fornecidas em regime de "melhor esforco". Pode haver atualizacoes sem notificacao
+e tambem pode haver notificacao sem que uma atualizacao real tenha acontecido. Os clientes ainda devem executar
+verificacoes periodicas por conta propria, embora com uma frequencia bem menor.
+
+## Requisitos
+
+Este app exige um servidor Redis configurado e o Nextcloud tambem precisa estar configurado para usar esse Redis.
+
+## Configuracao rapida
+
+O app inclui um assistente de configuracao que deve orientar a maior parte dos cenarios.
+
+- Instale o app "Client Push" (`notify_push`) pela App Store
+- Execute `occ notify_push:setup` e siga as instrucoes exibidas.
+ Se o assistente falhar, as instrucoes manuais estao abaixo.
+
+## Configuracao manual
+
+A configuracao envolve quatro etapas:
+
+- Instalar o app `notify_push` pela App Store
+- Configurar o servidor push
+- Configurar o proxy reverso
+- Configurar o app no Nextcloud
+
+> __Para usuarios do Nextcloud Snap:__ \
+> A equipe do snap publicou uma pagina na wiki explicando como instalar o Client Push no Nextcloud Snap.
+>
+> Veja [a pagina da wiki](https://github.com/nextcloud-snap/nextcloud-snap/wiki/Configure-HPB-client-push-for-Nextcloud-snap)!
+
+### Servidor push
+
+O servidor push deve rodar como daemon em segundo plano. A forma recomendada e configura-lo como servico do sistema no
+seu init system.
+Se voce nao usa systemd, qualquer sistema de init ou gerenciamento de processos que execute o binario do push server
+com as variaveis de ambiente descritas tambem funcionara.
+
+#### systemd
+
+Em setups baseados em systemd, crie o arquivo `/etc/systemd/system/notify_push.service` com o conteudo abaixo.
+
+```ini
+[Unit]
+After=network.target mariadb.service nginx.service postgresql.service redis.service
+Description = Push daemon for Nextcloud clients
+Documentation = https://github.com/nextcloud/notify_push
+
+[Service]
+# Altere se esta porta ja estiver em uso
+Environment = PORT=7867
+ExecStart = /path/to/push/binary/notify_push /path/to/nextcloud/config/config.php
+# exige que o push server tenha sido compilado com a feature systemd (habilitada por padrao)
+Type = notify
+User = www-data
+Restart = always
+RestartSec = 60
+
+[Install]
+WantedBy = multi-user.target
+```
+
+Se o push server nao tiver sido compilado com a feature opcional de systemd, remova a linha `Type=notify`.
+
+#### OpenRC
+
+Em setups baseados em OpenRC, crie o arquivo `/etc/init.d/notify_push` com o conteudo abaixo.
+
+```sh
+#!/sbin/openrc-run
+
+description="Push daemon for Nextcloud clients"
+
+output_log=${output_log:-/var/log/$RC_SVCNAME.log}
+pidfile=${pidfile:-/run/$RC_SVCNAME.pid}
+
+command=${command:-/path/to/push/binary/notify_push}
+command_user=${command_user:-www-data:www-data}
+command_args="--port 7867 /path/to/nextcloud/config/config.php"
+command_background=true
+
+depend() {
+ need net
+ use nginx php-fpm8 mariadb postgresql redis
+}
+
+start_pre() {
+ checkpath --file --owner $command_user $output_log
+}
+```
+
+Ajuste os caminhos, portas e usuario conforme necessario.
+
+#### Configuracao
+
+O push server pode ser configurado de duas formas: carregando os dados do `config.php` do Nextcloud ou definindo todas
+as opcoes via variaveis de ambiente.
+
+Reutilizar a configuracao do Nextcloud e o metodo recomendado, porque garante que tudo continue sincronizado.
+
+Se nao for possivel usar `config.php`, configure o push server com as seguintes variaveis:
+
+- `DATABASE_URL` URL de conexao com o banco do Nextcloud, por exemplo
+ `postgres://user:password@db_host/db_name`
+- `DATABASE_PREFIX` prefixo das tabelas configurado no Nextcloud, por exemplo `oc_`
+- `REDIS_URL` URL de conexao com o Redis, por exemplo `redis://redis_host`
+- `NEXTCLOUD_URL` URL da instancia Nextcloud, por exemplo `https://cloud.example.com`
+
+Voce tambem pode fornecer essas opcoes como argumentos de linha de comando. Consulte `notify_push --help`.
+
+Se a mesma opcao for definida em varias fontes, os argumentos de linha de comando sobrescrevem os valores das
+variaveis de ambiente, que por sua vez sobrescrevem os valores lidos de `config.php`.
+
+A porta em que o servidor escuta so pode ser configurada por `PORT` ou pelo argumento `--port`, e o padrao e `7867`.
+Alternativamente, voce pode fazer o servidor escutar em um socket Unix com `SOCKET_PATH` ou `--socket-path`.
+
+Observe que o Nextcloud carrega todos os arquivos `*.config.php` no diretorio de configuracao, alem do arquivo
+principal. Para reproduzir esse mesmo comportamento, use a opcao `--glob-config`.
+
+
+Usando uma instancia Redis separada para o push server
+
+
+Opcionalmente, voce pode usar uma instancia Redis separada para a comunicacao entre o servidor Nextcloud e o daemon
+push.
+
+Isso ajuda a deslocar a carga do Redis principal ou a usar um setup mais otimizado para esse uso especifico
+(`PUBSUB` em vez de cache).
+
+Voce pode configurar isso adicionando a opcao `notify_push_redis` no `config.php` do servidor Nextcloud. Ela aceita os
+mesmos parametros da configuracao Redis padrao.
+
+
+
+
+Conectando ao Redis via TLS
+
+
+Voce pode conectar ao Redis via TLS usando `rediss://` como URL do Redis.
+
+O certificado e a chave do cliente podem ser definidos pelos argumentos `--redis-tls-cert` e `--redis-tls-key`
+(ou pelas variaveis `REDIS_TLS_CERT` e `REDIS_TLS_KEY`).
+O certificado da autoridade certificadora para validar o certificado do servidor pode ser definido por
+`--redis-tls-ca` (ou `REDIS_TLS_CA`).
+
+Adicionalmente, voce pode desabilitar a validacao do hostname do certificado do servidor com
+`--redis-tls-dont-validate-hostname` ou desabilitar toda a validacao de certificados com
+`--redis-tls-insecure` (ou com as variaveis `REDIS_TLS_DONT_VALIDATE_HOSTNAME` e `REDIS_TLS_INSECURE`).
+
+
+
+#### Configuracao TLS
+
+O push server tambem pode servir trafego via TLS. Isso e pensado principalmente para proteger o trafego entre o push
+server e o proxy reverso quando eles estiverem em hosts diferentes. Rodar sem proxy reverso ou load balancer nao e
+recomendado.
+
+O TLS pode ser habilitado com os argumentos `--tls-cert` e `--tls-key` (ou pelas variaveis `TLS_CERT` e `TLS_KEY`).
+
+#### Iniciando o servico
+
+Depois que o arquivo do servico systemd estiver configurado corretamente, voce pode inicia-lo com:
+
+- systemd: `sudo systemctl start notify_push`
+- OpenRc: `sudo rc-service notify_push start`
+
+E habilitar o inicio automatico no boot com:
+
+- systemd: `sudo systemctl enable notify_push`
+- OpenRc: `sudo rc-update add notify_push`
+
+Sempre que este app receber uma atualizacao, reinicie o servico:
+
+- systemd: `sudo systemctl restart notify_push`
+- OpenRc: `sudo rc-service notify_push restart`
+
+
+Alternativamente, voce pode automatizar isso via systemctl criando o servico e o path abaixo (clique para expandir)
+
+Primeiro, crie um servico oneshot para reiniciar o daemon.
+
+`/etc/systemd/system/notify_push-watcher.service`
+
+```ini
+[Unit]
+Description = Restart Push daemon for Nextcloud clients when it receives updates
+Documentation = https://github.com/nextcloud/notify_push
+Requires = notify_push.service
+After = notify_push.service
+StartLimitIntervalSec = 10
+StartLimitBurst = 5
+
+[Service]
+Type = oneshot
+ExecStart = /usr/bin/systemctl restart notify_push.service
+
+[Install]
+WantedBy = multi-user.target
+```
+
+Depois, crie um `path` para acionar o restart sempre que o binario mudar.
+
+`/etc/systemd/system/notify_push-watcher.path`
+
+```ini
+[Unit]
+Description = Restart Push daemon for Nextcloud clients when it receives updates
+Documentation = https://github.com/nextcloud/notify_push
+PartOf = notify_push-watcher.service
+
+[Path]
+PathModified = /path/to/push/binary/notify_push
+Unit = notify_push-watcher.service
+
+[Install]
+WantedBy = multi-user.target
+```
+
+Ajuste o caminho conforme necessario.
+
+Por fim, habilite com:
+
+```bash
+sudo systemctl enable notify_push-watcher.path
+```
+
+
+
+### Proxy reverso
+
+E **fortemente** recomendado colocar o servico push atras de um proxy reverso. Isso evita expor uma porta adicional na
+internet e tambem faz a terminacao TLS para nao enviar credenciais em texto puro.
+
+Voce provavelmente pode usar o mesmo servidor web que ja atende seu Nextcloud.
+
+#### Nginx
+
+Se voce usa nginx, adicione o bloco `location` abaixo ao bloco `server` existente do Nextcloud.
+
+```nginx
+location ^~ /push/ {
+ proxy_pass http://127.0.0.1:7867/;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "Upgrade";
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+}
+```
+
+Observe que as duas barras finais sao obrigatorias.
+
+Depois de editar a configuracao do nginx, recarregue com:
+
+```bash
+sudo nginx -s reload
+```
+
+#### nginx-proxy
+
+Se voce usa o projeto [`nginx-proxy`](https://github.com/nginx-proxy/nginx-proxy) em Docker, nao adicione o bloco
+`location` manualmente no container do Nextcloud. Nesse caso, o `server` block e gerado pelo `nginx-proxy`, entao o
+jeito mais simples e publicar o `notify_push` no mesmo host e no caminho `/push/`.
+
+Exemplo usando o dominio generico `cloud.example.com`:
+
+```yaml
+services:
+ nextcloud:
+ image: nextcloud:apache
+ environment:
+ VIRTUAL_HOST: cloud.example.com
+ VIRTUAL_PATH: /
+ networks:
+ - reverse-proxy
+ - internal
+
+ notify_push:
+ image: ghcr.io/nextcloud/notify_push:latest
+ environment:
+ PORT: "7867"
+ VIRTUAL_HOST: cloud.example.com
+ VIRTUAL_PATH: /push/
+ VIRTUAL_DEST: /
+ VIRTUAL_PORT: "7867"
+ expose:
+ - "7867"
+ networks:
+ - reverse-proxy
+ - internal
+```
+
+Pontos importantes:
+
+- o `notify_push` precisa estar na mesma rede Docker do `nginx-proxy`
+- use `VIRTUAL_PATH=/push/` com barra final
+- use `VIRTUAL_DEST=/` para remover o prefixo `/push/` antes de encaminhar a requisicao
+- em setups com `nginx-proxy`, normalmente `expose` e suficiente e voce nao precisa publicar a porta `7867` no host
+
+Se preferir injetar configuracao manualmente no `nginx-proxy`, voce pode criar um arquivo em
+`/etc/nginx/vhost.d/cloud.example.com` com o bloco abaixo:
+
+```nginx
+location ^~ /push/ {
+ proxy_pass http://notify_push:7867/;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "Upgrade";
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+}
+```
+
+Observe novamente que as barras finais em `/push/` e no `proxy_pass` sao obrigatorias.
+
+#### Apache
+
+Para usar Apache como proxy reverso, primeiro habilite os modulos abaixo:
+
+```bash
+sudo a2enmod proxy
+sudo a2enmod proxy_http
+sudo a2enmod proxy_wstunnel
+```
+
+Depois adicione estas linhas ao bloco `` usado pelo servidor Nextcloud.
+
+```apacheconf
+ProxyPass /push/ws ws://127.0.0.1:7867/ws
+ProxyPass /push/ http://127.0.0.1:7867/
+ProxyPassReverse /push/ http://127.0.0.1:7867/
+```
+
+Em seguida, reinicie o Apache:
+
+```bash
+sudo systemctl restart apache2
+```
+
+#### Caddy v2
+
+```Caddyfile
+handle_path /push/* {
+ reverse_proxy http://127.0.0.1:7867
+}
+```
+
+### App do Nextcloud
+
+Depois que o push server e o proxy reverso estiverem configurados, habilite o app `notify_push` e informe onde o
+servidor push esta escutando.
+
+- habilite o app com `occ app:enable notify_push`
+- defina a URL do push server com `occ notify_push:setup https://cloud.example.com/push`
+
+O app executara alguns testes automaticamente para verificar se a configuracao esta correta.
+
+### Testes
+
+Depois de concluir a configuracao, teste nestes niveis:
+
+1. Configure a URL do push server no Nextcloud:
+
+```bash
+occ app:enable notify_push
+occ notify_push:setup https://cloud.example.com/push
+```
+
+2. Execute o autoteste:
+
+```bash
+occ notify_push:self-test
+```
+
+3. Verifique se o proxy responde corretamente:
+
+```bash
+curl -I https://cloud.example.com/push
+curl -I https://cloud.example.com/push/
+```
+
+O comportamento esperado e:
+
+- `/push` responder com `301` para `/push/`
+- `/push/` nao responder com `502` ou outro erro de upstream do proxy
+
+4. Gere uma notificacao no Nextcloud e acompanhe os logs do `notify_push`. Por exemplo, abra o Nextcloud em um
+navegador, mantenha a sessao ativa e crie uma notificacao, uma mensagem no Talk ou uma alteracao de arquivo. Em
+seguida, observe se o servico registra conexoes e eventos.
+
+Em Docker Compose:
+
+```bash
+docker compose logs -f notify_push
+```
+
+Se precisar de mais detalhes durante o teste, aumente temporariamente o nivel de log:
+
+```bash
+occ notify_push:log debug
+```
+
+Depois de concluir a verificacao, restaure o nivel anterior:
+
+```bash
+occ notify_push:log --restore
+```
+
+### Logs
+
+Por padrao, o push server registra apenas avisos. Voce pode alterar temporariamente o nivel de log com:
+
+```bash
+occ notify_push:log
+```
+
+Onde `level` pode ser `error`, `warn`, `info`, `debug` ou `trace`. Para restaurar o nivel anterior:
+
+```bash
+occ notify_push:log --restore
+```
+
+Alternativamente, voce pode definir o nivel de log pela variavel `LOG`.
+
+### Metricas
+
+O push server pode expor metricas basicas sobre numero de clientes conectados e trafego, definindo a variavel
+`METRICS_PORT`.
+Por padrao, o endpoint de metricas escuta em todas as interfaces (`0.0.0.0`).
+Tambem e possivel fazer o listener de metricas escutar em um socket Unix local usando `METRICS_SOCKET_PATH`.
+
+Depois disso, as metricas ficam disponiveis em formato compativel com Prometheus no endpoint `/metrics`, na porta ou
+socket configurado.
+
+Voce tambem pode consultar as metricas manualmente com `occ notify_push:metrics`, mesmo sem configurar
+`METRICS_PORT` ou `METRICS_SOCKET_PATH`.
+
+### Certificados autoassinados
+
+Se o seu Nextcloud usa certificado autoassinado, voce precisa definir `NEXTCLOUD_URL` para uma URL local nao HTTPS ou
+desabilitar a verificacao de certificado com `ALLOW_SELF_SIGNED=true`.
+
+## Troubleshooting
+
+Ao encontrar problemas, primeiro confirme que voce esta usando a release mais recente. O problema pode ja ter sido
+corrigido ou a versao nova pode trazer diagnosticos adicionais.
+
+### "push server is not a trusted proxy"
+
+- Garanta que voce nao adicionou uma lista `trusted_proxies` duplicada no `config.php`.
+- Se voce alterou `forwarded_for_headers`, garanta que `HTTP_X_FORWARDED_FOR` esteja incluido.
+- Se o hostname do seu Nextcloud resolve para um IP dinamico, tente definir `NEXTCLOUD_URL` com o IP interno do
+ servidor.
+
+ Alternativamente, editar `/etc/hosts` para apontar o dominio do Nextcloud para o IP interno pode funcionar em alguns
+ setups.
+- Se seu ambiente roda em Docker e os containers estao interligados, normalmente voce pode usar o nome do container do
+ Nextcloud como hostname em `NEXTCLOUD_URL`.
+
+## Desenvolvimento
+
+Para saber como usar o push server no seu proprio app ou cliente, consulte [DEVELOPING.md](./DEVELOPING.md)
+
+## Cliente de teste
+
+Para desenvolvimento e testes, existe um cliente de teste que pode ser baixado na
+[release atual](https://github.com/nextcloud/notify_push/releases/latest).
+(Clique no `test_client` da sua plataforma para baixar o binario.)
+
+```bash
+test_client https://cloud.example.com username password
+```
+
+Observe que ele nao suporta autenticacao de dois fatores nem fluxos de login nao padrao. Nesses casos, use uma senha
+de app.
diff --git a/deploy/nextcloud/README.md b/deploy/nextcloud/README.md
new file mode 100644
index 00000000..09efda83
--- /dev/null
+++ b/deploy/nextcloud/README.md
@@ -0,0 +1,90 @@
+
+
+# Implantacao em um stack Nextcloud existente
+
+Estes arquivos reproduzem a configuracao necessaria para executar o `notify_push`
+em Docker quando Nextcloud, PostgreSQL, Redis e Nginx ja existem.
+
+## 1. Configure as conexoes
+
+Copie o modelo para o diretorio do Compose do Nextcloud:
+
+```bash
+cp env.notify-push.example /caminho/do/nextcloud/.env.notify_push
+chmod 0600 /caminho/do/nextcloud/.env.notify_push
+```
+
+Edite `DATABASE_URL`, `DATABASE_PREFIX`, `REDIS_URL` e `NEXTCLOUD_URL`.
+A senha inserida em `DATABASE_URL` deve usar percent-encoding. Para codificar
+somente a senha:
+
+```bash
+php -r 'echo rawurlencode($argv[1]), PHP_EOL;' 'SENHA'
+```
+
+Nao inclua `.env.notify_push` no Git.
+
+## 2. Confira os nomes das redes
+
+O overlay usa por padrao as redes Docker `internal`, `postgres` e `redis`.
+Quando os nomes forem diferentes, defina no arquivo `.env` principal do stack:
+
+```dotenv
+NEXTCLOUD_NETWORK=nome_da_rede_nextcloud
+POSTGRES_NETWORK=nome_da_rede_postgres
+REDIS_NETWORK=nome_da_rede_redis
+```
+
+Os hostnames usados em `DATABASE_URL` e `REDIS_URL` tambem precisam resolver
+nessas redes.
+
+## 3. Adicione a rota ao Nginx
+
+Copie `nginx.notify-push.conf` para um diretorio incluido dentro do bloco
+`server` do Nginx. Em um serviço `web` com includes montados, por exemplo:
+
+```yaml
+services:
+ web:
+ volumes:
+ - ./volumes/nginx/includes:/etc/nginx/conf.d/includes:ro
+```
+
+```bash
+cp nginx.notify-push.conf \
+ /caminho/do/nextcloud/volumes/nginx/includes/notify_push.conf
+```
+
+O Nginx e o `notify_push` precisam compartilhar a rede configurada em
+`NEXTCLOUD_NETWORK`.
+
+## 4. Inicie e valide
+
+Execute a partir do diretorio do stack Nextcloud, informando o overlay:
+
+```bash
+docker compose \
+ -f docker-compose.yml \
+ -f docker-compose.override.yml \
+ -f /caminho/do/notify_push/deploy/nextcloud/compose.notify-push.yml \
+ up -d notify_push
+
+docker compose exec -T web nginx -t
+docker compose exec -T web nginx -s reload
+docker compose exec -T app php occ notify_push:setup https://cloud.example.com/push
+docker compose exec -T app php occ notify_push:self-test
+```
+
+O resultado esperado do ultimo comando e:
+
+```text
+✓ redis is configured
+✓ push server is receiving redis messages
+✓ push server can load mount info from database
+✓ push server can connect to the Nextcloud server
+✓ push server is a trusted proxy
+✓ push server is running the same version as the app
+```
diff --git a/deploy/nextcloud/compose.notify-push.yml b/deploy/nextcloud/compose.notify-push.yml
new file mode 100644
index 00000000..c49f7337
--- /dev/null
+++ b/deploy/nextcloud/compose.notify-push.yml
@@ -0,0 +1,40 @@
+# SPDX-FileCopyrightText: 2026 LibreCode contributors
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+# Overlay para uma instalacao Nextcloud que ja possui PostgreSQL, Redis e
+# um proxy web nas redes indicadas abaixo.
+services:
+ notify_push:
+ image: "${NOTIFY_PUSH_IMAGE:-ghcr.io/librecodecoop/notify_push:edge}"
+ restart: unless-stopped
+ env_file:
+ - .env.notify_push
+ environment:
+ PORT: "7867"
+ expose:
+ - "7867"
+ networks:
+ - nextcloud
+ - postgres
+ - redis
+ labels:
+ - com.centurylinklabs.watchtower.enable=true
+ deploy:
+ resources:
+ limits:
+ cpus: "2"
+ memory: 256M
+ reservations:
+ cpus: "0.25"
+ memory: 64M
+
+networks:
+ nextcloud:
+ external: true
+ name: "${NEXTCLOUD_NETWORK:-internal}"
+ postgres:
+ external: true
+ name: "${POSTGRES_NETWORK:-postgres}"
+ redis:
+ external: true
+ name: "${REDIS_NETWORK:-redis}"
diff --git a/deploy/nextcloud/env.notify-push.example b/deploy/nextcloud/env.notify-push.example
new file mode 100644
index 00000000..a1083408
--- /dev/null
+++ b/deploy/nextcloud/env.notify-push.example
@@ -0,0 +1,13 @@
+# SPDX-FileCopyrightText: 2026 LibreCode contributors
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+# Copie para .env.notify_push e aplique chmod 0600.
+# A senha precisa estar codificada para uso em URL (percent-encoding).
+DATABASE_URL=postgres://nextcloud:URL_ENCODED_PASSWORD@postgres/nextcloud
+
+# Use uma string vazia quando a instalacao nao possuir prefixo.
+DATABASE_PREFIX=
+
+REDIS_URL=redis://redis:6379/0
+NEXTCLOUD_URL=https://cloud.example.com
+LOG=info
diff --git a/deploy/nextcloud/nginx.notify-push.conf b/deploy/nextcloud/nginx.notify-push.conf
new file mode 100644
index 00000000..307c3218
--- /dev/null
+++ b/deploy/nextcloud/nginx.notify-push.conf
@@ -0,0 +1,12 @@
+# SPDX-FileCopyrightText: 2026 LibreCode contributors
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+location ^~ /push/ {
+ proxy_pass http://notify_push:7867/;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "Upgrade";
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+}
diff --git a/docker-compose.example.yml b/docker-compose.example.yml
new file mode 100644
index 00000000..3d08bc39
--- /dev/null
+++ b/docker-compose.example.yml
@@ -0,0 +1,17 @@
+# SPDX-FileCopyrightText: 2026 LibreCode contributors
+# SPDX-License-Identifier: AGPL-3.0-or-later
+services:
+ notify_push:
+ image: ghcr.io/librecodecoop/notify_push:edge
+ build:
+ context: .
+ restart: unless-stopped
+ ports:
+ - "${NOTIFY_PUSH_PORT:-7867}:7867"
+ environment:
+ PORT: "7867"
+ NEXTCLOUD_URL: "https://cloud.example.com"
+ DATABASE_URL: "postgres://nextcloud:change-me@db/nextcloud"
+ DATABASE_PREFIX: "oc_"
+ REDIS_URL: "redis://redis:6379/0"
+ LOG: "info"
diff --git a/docs/docker-ghcr.md b/docs/docker-ghcr.md
new file mode 100644
index 00000000..983836fc
--- /dev/null
+++ b/docs/docker-ghcr.md
@@ -0,0 +1,108 @@
+
+
+# Docker and GHCR
+
+This repository already ships a multi-stage `Dockerfile` that compiles `notify_push` as a static binary and copies it
+into a minimal runtime image.
+
+## Build locally
+
+```bash
+docker build -t notify_push:local .
+```
+
+## Run locally
+
+You can run the container in two supported ways.
+
+### Option 1: Load the Nextcloud `config.php`
+
+This is the recommended option because `notify_push` reuses the same database and redis configuration already used by
+Nextcloud.
+
+```bash
+docker run --rm \
+ -p 7867:7867 \
+ -e PORT=7867 \
+ -v /path/to/nextcloud/config:/config:ro \
+ notify_push:local \
+ /config/config.php
+```
+
+### Option 2: Configure everything with environment variables
+
+```bash
+docker run --rm \
+ -p 7867:7867 \
+ -e PORT=7867 \
+ -e NEXTCLOUD_URL=https://cloud.example.com \
+ -e DATABASE_URL=postgres://nextcloud:change-me@db/nextcloud \
+ -e DATABASE_PREFIX=oc_ \
+ -e REDIS_URL=redis://redis:6379/0 \
+ -e LOG=info \
+ notify_push:local
+```
+
+## Docker Compose example
+
+An example service definition is available in [`docker-compose.example.yml`](../docker-compose.example.yml).
+
+```bash
+cp docker-compose.example.yml docker-compose.yml
+docker compose up -d --build
+docker compose logs -f notify_push
+```
+
+For an existing Dockerized Nextcloud installation, use the production overlay,
+environment template, and Nginx route in
+[`deploy/nextcloud/`](../deploy/nextcloud/README.md). That example attaches the
+push server to the same Nextcloud, PostgreSQL, and Redis networks and keeps the
+database URL outside the Compose file.
+
+If you prefer using `config.php` in Compose, mount the config directory and add:
+
+```yaml
+command:
+ - /config/config.php
+volumes:
+ - /path/to/nextcloud/config:/config:ro
+```
+
+The path configured as `datadirectory` must also exist inside the container at
+the same absolute path. For Dockerized Nextcloud installations, environment
+variables are usually less error-prone than parsing `config.php`.
+
+## Reverse proxy and app setup
+
+After the container is running, configure the reverse proxy exactly as described in the main
+[`README.md`](../README.md), then point the app to the public push endpoint:
+
+```bash
+occ notify_push:setup https://cloud.example.com/push
+```
+
+## Publish manually to GHCR
+
+Replace `librecodecoop` with your GitHub org or username if needed.
+
+```bash
+docker build -t ghcr.io/librecodecoop/notify_push:dev .
+echo "$GHCR_TOKEN" | docker login ghcr.io -u YOUR_GITHUB_USER --password-stdin
+docker push ghcr.io/librecodecoop/notify_push:dev
+```
+
+The token needs `write:packages`. If the package should be pulled by anonymous users, set the package visibility to
+public in GitHub after the first push.
+
+## Automatic publication with GitHub Actions
+
+The workflow in `.github/workflows/docker-ghcr.yml` does the following:
+
+- Builds the Docker image on pull requests.
+- Publishes `edge` and branch tags to `ghcr.io//` on pushes to `main`.
+- Publishes version tags when a Git tag like `v1.2.3` is pushed.
+
+GitHub's built-in `GITHUB_TOKEN` is enough as long as the workflow has `packages: write`.
diff --git a/vendor-bin/phpunit/composer.lock b/vendor-bin/phpunit/composer.lock
index 1b3e3297..827259ae 100644
--- a/vendor-bin/phpunit/composer.lock
+++ b/vendor-bin/phpunit/composer.lock
@@ -79,16 +79,16 @@
},
{
"name": "myclabs/deep-copy",
- "version": "1.12.1",
+ "version": "1.13.4",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
- "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845"
+ "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845",
- "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a",
+ "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a",
"shasum": ""
},
"require": {
@@ -127,7 +127,7 @@
],
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
- "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1"
+ "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4"
},
"funding": [
{
@@ -135,20 +135,20 @@
"type": "tidelift"
}
],
- "time": "2024-11-08T17:47:46+00:00"
+ "time": "2025-08-01T08:46:24+00:00"
},
{
"name": "nikic/php-parser",
- "version": "v5.4.0",
+ "version": "v5.7.0",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
- "reference": "447a020a1f875a434d62f2a401f53b82a396e494"
+ "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494",
- "reference": "447a020a1f875a434d62f2a401f53b82a396e494",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82",
+ "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82",
"shasum": ""
},
"require": {
@@ -167,7 +167,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "5.0-dev"
+ "dev-master": "5.x-dev"
}
},
"autoload": {
@@ -191,9 +191,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
- "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0"
+ "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0"
},
- "time": "2024-12-30T11:07:19+00:00"
+ "time": "2025-12-06T11:56:16+00:00"
},
{
"name": "phar-io/manifest",
@@ -634,16 +634,16 @@
},
{
"name": "phpunit/phpunit",
- "version": "9.6.22",
+ "version": "9.6.33",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c"
+ "reference": "fea06253ecc0a32faf787bd31b261f56f351d049"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f80235cb4d3caa59ae09be3adf1ded27521d1a9c",
- "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fea06253ecc0a32faf787bd31b261f56f351d049",
+ "reference": "fea06253ecc0a32faf787bd31b261f56f351d049",
"shasum": ""
},
"require": {
@@ -654,7 +654,7 @@
"ext-mbstring": "*",
"ext-xml": "*",
"ext-xmlwriter": "*",
- "myclabs/deep-copy": "^1.12.1",
+ "myclabs/deep-copy": "^1.13.4",
"phar-io/manifest": "^2.0.4",
"phar-io/version": "^3.2.1",
"php": ">=7.3",
@@ -665,11 +665,11 @@
"phpunit/php-timer": "^5.0.3",
"sebastian/cli-parser": "^1.0.2",
"sebastian/code-unit": "^1.0.8",
- "sebastian/comparator": "^4.0.8",
+ "sebastian/comparator": "^4.0.10",
"sebastian/diff": "^4.0.6",
"sebastian/environment": "^5.1.5",
- "sebastian/exporter": "^4.0.6",
- "sebastian/global-state": "^5.0.7",
+ "sebastian/exporter": "^4.0.8",
+ "sebastian/global-state": "^5.0.8",
"sebastian/object-enumerator": "^4.0.4",
"sebastian/resource-operations": "^3.0.4",
"sebastian/type": "^3.2.1",
@@ -717,7 +717,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
- "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.22"
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.33"
},
"funding": [
{
@@ -728,12 +728,20 @@
"url": "https://github.com/sebastianbergmann",
"type": "github"
},
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
{
"url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit",
"type": "tidelift"
}
],
- "time": "2024-12-05T13:48:26+00:00"
+ "time": "2026-01-27T05:25:09+00:00"
},
{
"name": "sebastian/cli-parser",
@@ -904,16 +912,16 @@
},
{
"name": "sebastian/comparator",
- "version": "4.0.8",
+ "version": "4.0.10",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
- "reference": "fa0f136dd2334583309d32b62544682ee972b51a"
+ "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a",
- "reference": "fa0f136dd2334583309d32b62544682ee972b51a",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e4df00b9b3571187db2831ae9aada2c6efbd715d",
+ "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d",
"shasum": ""
},
"require": {
@@ -966,15 +974,27 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/comparator/issues",
- "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8"
+ "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.10"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator",
+ "type": "tidelift"
}
],
- "time": "2022-09-14T12:41:17+00:00"
+ "time": "2026-01-24T09:22:56+00:00"
},
{
"name": "sebastian/complexity",
@@ -1164,16 +1184,16 @@
},
{
"name": "sebastian/exporter",
- "version": "4.0.6",
+ "version": "4.0.8",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
- "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72"
+ "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72",
- "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/14c6ba52f95a36c3d27c835d65efc7123c446e8c",
+ "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c",
"shasum": ""
},
"require": {
@@ -1229,28 +1249,40 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues",
- "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6"
+ "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.8"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter",
+ "type": "tidelift"
}
],
- "time": "2024-03-02T06:33:00+00:00"
+ "time": "2025-09-24T06:03:27+00:00"
},
{
"name": "sebastian/global-state",
- "version": "5.0.7",
+ "version": "5.0.8",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
- "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9"
+ "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9",
- "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b6781316bdcd28260904e7cc18ec983d0d2ef4f6",
+ "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6",
"shasum": ""
},
"require": {
@@ -1293,15 +1325,27 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/global-state/issues",
- "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7"
+ "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.8"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state",
+ "type": "tidelift"
}
],
- "time": "2024-03-02T06:35:11+00:00"
+ "time": "2025-08-10T07:10:35+00:00"
},
{
"name": "sebastian/lines-of-code",
@@ -1474,16 +1518,16 @@
},
{
"name": "sebastian/recursion-context",
- "version": "4.0.5",
+ "version": "4.0.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/recursion-context.git",
- "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1"
+ "reference": "539c6691e0623af6dc6f9c20384c120f963465a0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1",
- "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/539c6691e0623af6dc6f9c20384c120f963465a0",
+ "reference": "539c6691e0623af6dc6f9c20384c120f963465a0",
"shasum": ""
},
"require": {
@@ -1525,15 +1569,27 @@
"homepage": "https://github.com/sebastianbergmann/recursion-context",
"support": {
"issues": "https://github.com/sebastianbergmann/recursion-context/issues",
- "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5"
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.6"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context",
+ "type": "tidelift"
}
],
- "time": "2023-02-03T06:07:39+00:00"
+ "time": "2025-08-10T06:57:39+00:00"
},
{
"name": "sebastian/resource-operations",
@@ -1700,16 +1756,16 @@
},
{
"name": "theseer/tokenizer",
- "version": "1.2.3",
+ "version": "1.3.1",
"source": {
"type": "git",
"url": "https://github.com/theseer/tokenizer.git",
- "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2"
+ "reference": "b7489ce515e168639d17feec34b8847c326b0b3c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
- "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c",
+ "reference": "b7489ce515e168639d17feec34b8847c326b0b3c",
"shasum": ""
},
"require": {
@@ -1738,7 +1794,7 @@
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
"support": {
"issues": "https://github.com/theseer/tokenizer/issues",
- "source": "https://github.com/theseer/tokenizer/tree/1.2.3"
+ "source": "https://github.com/theseer/tokenizer/tree/1.3.1"
},
"funding": [
{
@@ -1746,7 +1802,7 @@
"type": "github"
}
],
- "time": "2024-03-03T12:36:25+00:00"
+ "time": "2025-11-17T20:03:58+00:00"
}
],
"aliases": [],
@@ -1759,5 +1815,5 @@
"platform-overrides": {
"php": "8.0"
},
- "plugin-api-version": "2.6.0"
+ "plugin-api-version": "2.9.0"
}
diff --git a/vendor-bin/psalm/composer.lock b/vendor-bin/psalm/composer.lock
index 81815631..8e65a168 100644
--- a/vendor-bin/psalm/composer.lock
+++ b/vendor-bin/psalm/composer.lock
@@ -1300,16 +1300,16 @@
},
{
"name": "symfony/deprecation-contracts",
- "version": "v3.5.1",
+ "version": "v3.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
- "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6"
+ "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
- "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62",
+ "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62",
"shasum": ""
},
"require": {
@@ -1322,7 +1322,7 @@
"name": "symfony/contracts"
},
"branch-alias": {
- "dev-main": "3.5-dev"
+ "dev-main": "3.6-dev"
}
},
"autoload": {
@@ -1347,7 +1347,7 @@
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1"
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0"
},
"funding": [
{
@@ -1363,7 +1363,7 @@
"type": "tidelift"
}
],
- "time": "2024-09-25T14:20:29+00:00"
+ "time": "2024-09-25T14:21:43+00:00"
},
{
"name": "symfony/filesystem",
@@ -1433,16 +1433,16 @@
},
{
"name": "symfony/http-foundation",
- "version": "v5.4.48",
+ "version": "v5.4.50",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
- "reference": "3f38b8af283b830e1363acd79e5bc3412d055341"
+ "reference": "1a0706e8b8041046052ea2695eb8aeee04f97609"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3f38b8af283b830e1363acd79e5bc3412d055341",
- "reference": "3f38b8af283b830e1363acd79e5bc3412d055341",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/1a0706e8b8041046052ea2695eb8aeee04f97609",
+ "reference": "1a0706e8b8041046052ea2695eb8aeee04f97609",
"shasum": ""
},
"require": {
@@ -1489,7 +1489,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/http-foundation/tree/v5.4.48"
+ "source": "https://github.com/symfony/http-foundation/tree/v5.4.50"
},
"funding": [
{
@@ -1500,12 +1500,16 @@
"url": "https://github.com/fabpot",
"type": "github"
},
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2024-11-13T18:58:02+00:00"
+ "time": "2025-11-03T12:58:48+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -1747,19 +1751,20 @@
},
{
"name": "symfony/polyfill-mbstring",
- "version": "v1.31.0",
+ "version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341"
+ "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341",
- "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
+ "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
"shasum": ""
},
"require": {
+ "ext-iconv": "*",
"php": ">=7.2"
},
"provide": {
@@ -1807,7 +1812,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0"
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0"
},
"funding": [
{
@@ -1818,25 +1823,29 @@
"url": "https://github.com/fabpot",
"type": "github"
},
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2024-09-09T11:45:10+00:00"
+ "time": "2024-12-23T08:48:59+00:00"
},
{
"name": "symfony/polyfill-php80",
- "version": "v1.31.0",
+ "version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
- "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8"
+ "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
- "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
+ "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
"shasum": ""
},
"require": {
@@ -1887,7 +1896,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0"
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0"
},
"funding": [
{
@@ -1898,12 +1907,16 @@
"url": "https://github.com/fabpot",
"type": "github"
},
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2024-09-09T11:45:10+00:00"
+ "time": "2025-01-02T08:10:11+00:00"
},
{
"name": "symfony/service-contracts",
@@ -2253,5 +2266,5 @@
"platform-overrides": {
"php": "8.1"
},
- "plugin-api-version": "2.6.0"
+ "plugin-api-version": "2.9.0"
}