Multi-tenant natural language to SQL via Slack. Users ask questions in Slack, ThreadQL converts them to SQL queries using LLMs, executes them against configured datasources, and returns formatted results.
- Documentation: threadql.com
- GitHub: github.com/emtay-com/threadql
- Docker & Docker Compose
- PHP 8.4, Composer, Node.js (for running outside Docker)
make up # Start all services
make bash # Shell into the app container
make test # Run tests
make ecsfix # Fix code stylecomposer dev # Starts artisan serve, queue:listen, pail (logs), and npm dev (vite)See CLAUDE.md for full development documentation.
- A Kubernetes cluster (k3s, EKS, GKE, DOKS, etc.)
- Helm 3.x installed
Get the external IP:
kubectl get svc -l app.kubernetes.io/name=kubernetes-ingressCreate a DNS A record pointing your domain to the EXTERNAL-IP.
helm repo add emtay https://emtay-com.github.io/helm-charts
helm repo updatecert-manager must be installed before deploying ThreadQL. It manages TLS certificates via Let's Encrypt.
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml
kubectl wait --for=condition=Available deploy --all -n cert-manager --timeout=120sVerify it's running:
kubectl get pods -n cert-managerAll three pods (cert-manager, cert-manager-cainjector, cert-manager-webhook) should be Running and Ready.
Copy the appropriate example and fill in your values:
Cloud providers (no built-in ingress controller) — uses HAProxy:
cp helm/threadql/haproxy.values.yaml.example helm/threadql/my-values.yamlk3s (ships with Traefik):
cp helm/threadql/traefik.values.yaml.example helm/threadql/my-values.yamlEdit my-values.yaml and set:
| Value | Description |
|---|---|
mysql.rootPassword |
MySQL root password |
ingress.hosts[0].host |
Your domain (e.g. threadql.example.com) |
ingress.tls[0].hosts[0] |
Same domain |
certManager.email |
Email for Let's Encrypt notifications |
env.APP_KEY |
Generate with php artisan key:generate --show |
env.APP_URL |
https://your-domain.com |
env.JWT_SECRET |
Random 64-character string |
env.MASTER_ADMIN_PASSWORD |
Admin panel password |
helm upgrade --install app emtay/threadql \
-f helm/threadql/my-values.yamlOr from the local chart:
helm dependency build helm/threadql
helm upgrade --install app helm/threadql \
-f helm/threadql/values.yaml \
-f helm/threadql/my-values.yamlThe TLS certificate will be issued automatically after rollout once DNS propagates (typically 1-5 minutes).
kubectl get certificate # Should show READY=True
kubectl get clusterissuer # Should show READY=True
curl -I https://your-domain.com # Should return 200 with valid certIf the certificate is not issued:
kubectl describe certificate threadql-tls
kubectl describe challenge -A
kubectl get order -A
kubectl logs -n cert-manager deploy/cert-managerCommon issues:
- "ClusterIssuer not found" — cert-manager was not installed before deploying. Install it and redeploy.
- Challenge times out — The solver
ingressClassNameincertManager.solversmust matchingress.className. If you use HAProxy, both must behaproxy. If Traefik, both must betraefik. - Webhook errors on install — cert-manager's webhook needs a few seconds after installation. Wait and retry:
kubectl wait --for=condition=Available deploy --all -n cert-manager --timeout=120s
The Helm chart deploys:
| Component | Description |
|---|---|
| App (nginx + php-fpm) | Laravel application |
| Worker | Background queue processor |
| Redis | Queue and cache backend |
| MySQL | Primary database (optional — can use external) |
| HAProxy | Ingress controller (optional — for clusters without one) |
| ClusterIssuer | Let's Encrypt certificate issuer (when certManager.enabled) |
# Check pod status
kubectl get pods
# View app logs
kubectl logs deploy/app-threadql -c php
# Run artisan commands
kubectl exec deploy/app-threadql -c php -- php artisan migrate
# Restart after config changes
kubectl rollout restart deploy/app-threadqlFor full deployment documentation, see threadql.com.
This project is licensed under the MIT License. See LICENSE.



