{"id":50965761,"url":"https://github.com/obaidullah-faruk/noxrel","last_synced_at":"2026-06-18T20:02:23.645Z","repository":{"id":361420193,"uuid":"1250113621","full_name":"obaidullah-faruk/noxrel","owner":"obaidullah-faruk","description":"End-to-end video platform - upload, transcode, stream and monetize at scale","archived":false,"fork":false,"pushed_at":"2026-06-07T12:18:06.000Z","size":4465,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-07T14:12:00.444Z","etag":null,"topics":["apache-kafka","django-rest-framework","docker","fasitfy","ffmpeg","hls","localstack","nodejs","postgresql","python3","redis"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/obaidullah-faruk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-26T10:13:45.000Z","updated_at":"2026-06-07T12:18:10.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/obaidullah-faruk/noxrel","commit_stats":null,"previous_names":["obaidullah-faruk/noxrel"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/obaidullah-faruk/noxrel","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obaidullah-faruk%2Fnoxrel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obaidullah-faruk%2Fnoxrel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obaidullah-faruk%2Fnoxrel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obaidullah-faruk%2Fnoxrel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/obaidullah-faruk","download_url":"https://codeload.github.com/obaidullah-faruk/noxrel/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obaidullah-faruk%2Fnoxrel/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34505423,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-18T02:00:06.871Z","response_time":128,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apache-kafka","django-rest-framework","docker","fasitfy","ffmpeg","hls","localstack","nodejs","postgresql","python3","redis"],"created_at":"2026-06-18T20:02:22.585Z","updated_at":"2026-06-18T20:02:23.630Z","avatar_url":"https://github.com/obaidullah-faruk.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Noxrel: Video Streaming Platform\n\nA cloud-native, event-driven video streaming platform built as a microservices monorepo.\n\n## Quick Start\n\nChoose one of two ways to run the backend services:\n\n| | [Option A — Docker Compose](#option-a--docker-compose) | [Option B — Kubernetes (minikube)](#option-b--kubernetes-minikube) |\n|---|---|---|\n| **Best for** | Simple local dev, first-time setup | Learning K8s with real services |\n| **What runs in Docker** | Everything | Infrastructure only (Postgres, Redis, Kafka, LocalStack, observability) |\n| **What runs in K8s** | — | App services + nginx Ingress |\n| **Extra tools needed** | Docker Desktop | Docker Desktop + kubectl + minikube |\n\nFrontend dev servers (`web-user`, `web-admin`) are the same for both options — see [Frontend](#frontend) below.\n\n---\n\n### Common setup (both options)\n\nComplete these steps before following either Option A or Option B.\n\n#### 1. Install git hooks (once per clone)\n\n```bash\nmake install-hooks\n```\n\nThis installs [pre-commit](https://pre-commit.com/) hooks that run automatically on every `git commit`.\n\n#### 2. Copy environment files\n\nEach service, frontend, and the infrastructure stack ships an `.env.example`. Copy it to the appropriate local file before starting anything:\n\n```bash\n# Infrastructure stack (Elasticsearch/Kibana/Postgres/Grafana dev credentials)\ncp infrastructure/.env.example infrastructure/.env\n\n# Backend services (copy to .env)\ncp services/user-service/.env.example      services/user-service/.env\ncp services/video-service/.env.example     services/video-service/.env\ncp services/transcode-worker/.env.example  services/transcode-worker/.env\ncp services/streaming-service/.env.example services/streaming-service/.env\ncp services/billing-service/.env.example   services/billing-service/.env\n\n# Frontends (copy to .env.local)\ncp frontend/web-user/.env.example  frontend/web-user/.env.local\ncp frontend/web-admin/.env.example frontend/web-admin/.env.local\n```\n\nEdit each file to fill in secrets (JWT keys, etc.) before the first run.\n\n#### Stripe keys (billing-service)\n\n`services/billing-service/.env` contains two placeholders that **cannot be auto-generated** — you must supply real Stripe test-mode keys:\n\n| Variable | Where to get it |\n|---|---|\n| `STRIPE_SECRET_KEY` | [dashboard.stripe.com/test/apikeys](https://dashboard.stripe.com/test/apikeys) — copy the **Secret key** (`sk_test_…`) |\n| `STRIPE_WEBHOOK_SECRET` | Run `stripe listen --forward-to localhost:8003/api/v1/billing/webhooks/stripe` — the CLI prints the signing secret (`whsec_…`) |\n\nAll other values in `.env.example` files work as-is for local dev — only these two Stripe keys require manual input. The billing-service will start without them, but checkout and webhooks will fail until they are set.\n\n\nOnce done, continue with **[Option A](#option-a--docker-compose)** or **[Option B](#option-b--kubernetes-minikube)**.\n\n---\n\n### Option A — Docker Compose\n\nEverything (infrastructure + all services + Kong) runs in Docker Compose. Kong is available at `localhost:8100` — the frontend `.env.local` files already point there by default.\n\n#### 3a. Start everything\n\n```bash\nmake up\n```\n\n#### Stop (Docker)\n\n```bash\nmake down\n```\n\n---\n\n### Option B — Kubernetes (minikube)\n\nInfrastructure stays in Docker Compose. The four app services run as pods in a local minikube cluster, with nginx Ingress handling routing.\n\n```\n┌──────────────────────────────────────────────────────────┐\n│  Your local machine                                      │\n│                                                          │\n│  Docker Compose (make infra-up)                          │\n│    postgres:5432   redis:6379   kafka:9092/9094           │\n│    localstack:4566  elasticsearch:9200                   │\n│    prometheus  grafana  kibana  jaeger                   │\n│                                                          │\n│  Minikube cluster                                        │\n│    user-service      ─┐                                  │\n│    video-service      ├─→ host.docker.internal (infra)   │\n│    streaming-service  │                                  │\n│    transcode-worker  ─┘                                  │\n│    nginx Ingress (port 80) ← browser / curl / frontend   │\n└──────────────────────────────────────────────────────────┘\n```\n\n#### Prerequisites\n\nInstall kubectl, minikube, and k9s (optional TUI for cluster browsing):\n\n```bash\n# macOS\nbrew install kubectl minikube k9s\n\n# Linux\ncurl -LO \"https://dl.k8s.io/release/$(curl -sL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl\"\ncurl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64\nsudo install minikube-linux-amd64 /usr/local/bin/minikube\n```\n\n#### 3b. Start minikube and enable nginx Ingress\n\n```bash\nminikube start --cpus=3 --memory=4096 --driver=docker\nmake k8s-ingress-setup\n```\n\n#### 4b. Start infrastructure (Docker Compose)\n\n```bash\nmake infra-up\n```\n\nVerify all infra containers are healthy:\n\n```bash\nmake ps\n```\n\n#### 5b. Build service images inside minikube\n\n```bash\nmake k8s-build\n```\n\nThis points the Docker CLI at minikube's internal daemon and builds all four service images there. The images never touch Docker Hub.\n\n#### 6b. Deploy to the cluster\n\n```bash\nmake k8s-up\n```\n\nWatch pods start (Django services run DB migrations in an init container first):\n\n```bash\nmake k8s-status\n# or live:\nkubectl get pods -n platform -w\n```\n\nAll pods should reach `Running` / `READY 1/1` within ~2 minutes.\n\n#### 7b. Point the frontends at the nginx Ingress\n\nIn Kubernetes mode the API is served by the nginx Ingress inside the cluster, not by Docker Compose on `localhost:8100`. How you reach it depends on your minikube driver.\n\n**macOS / Windows (Docker driver) — use port-forward.** On the Docker driver the minikube IP (e.g. `192.168.49.2`) lives inside the Docker VM and is **not routable from your host or browser** — pointing the env files at it will make every request hang ~11s and then fail with a `502`. Forward the Ingress controller to `localhost:8100` instead and leave the env files at their Docker Compose default:\n\n```bash\n# Keep this running in its own terminal while you use the apps.\nkubectl port-forward -n ingress-nginx svc/ingress-nginx-controller 8100:80\n```\n\n`frontend/web-user/.env.local` and `frontend/web-admin/.env.local` already point at `http://localhost:8100`, so no edit is needed — the port-forward makes the cluster Ingress answer there.\n\n**Linux (or hyperkit/qemu/kvm drivers) — the minikube IP is host-routable**, so you can point the env files straight at it:\n\n```bash\nMINIKUBE_IP=$(minikube ip)\nsed -i.bak \"s|http://localhost:8100|http://$MINIKUBE_IP|g\" frontend/web-user/.env.local\nsed -i.bak \"s|http://localhost:8100|http://$MINIKUBE_IP|g\" frontend/web-admin/.env.local\n# Revert to localhost:8100 (and delete the .env.local.bak files) when switching back to Docker Compose.\n```\n\n\u003e **Next.js reads `.env.local` only at startup.** After changing either env file — or after starting the port-forward — **restart the dev server** (`npm run dev`) or the old gateway URL stays baked in and requests keep failing.\n\n#### 8b. Hit the API through nginx Ingress\n\n```bash\nMINIKUBE_IP=$(minikube ip)\n\n# Register a user\ncurl -X POST http://$MINIKUBE_IP/api/v1/auth/register/ \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"username\":\"testuser\",\"email\":\"test@example.com\",\"password\":\"testpass123\"}'\n\n# Login\ncurl -X POST http://$MINIKUBE_IP/api/v1/auth/login/ \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"username\":\"testuser\",\"password\":\"testpass123\"}'\n```\n\nOn the Docker driver, reach these same endpoints through the port-forward from 7b (`localhost:8100`) instead of the minikube IP:\n\n```bash\ncurl -X POST http://localhost:8100/api/v1/auth/login/ \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"username\":\"testuser\",\"password\":\"testpass123\"}'\n```\n\n#### Kubernetes workflow commands\n\n| Command | What it does |\n|---|---|\n| `make k8s-ingress-setup` | Enable nginx Ingress addon in minikube (once per cluster) |\n| `make k8s-build` | Build all 4 service images inside minikube |\n| `make k8s-up` | Apply all K8s manifests (namespace, configmaps, secrets, services, ingress) |\n| `make k8s-down` | Delete all services/configmaps/secrets/ingress from the cluster |\n| `make k8s-status` | Show pod status in the `platform` namespace |\n| `make k8s-logs SVC=user-service` | Tail logs for a service |\n| `make k8s-restart SVC=user-service` | Rolling restart after a configmap/secret change |\n\n#### After changing a ConfigMap or Secret\n\n```bash\n# Re-apply the changed file\nkubectl apply -f infrastructure/k8s/configmaps/user-service.yml\n\n# Trigger a rolling restart so pods pick up the new values\nmake k8s-restart SVC=user-service\n```\n\n#### Stop (Kubernetes)\n\n```bash\nmake k8s-down       # remove services from cluster\nmake infra-down     # stop Docker Compose infrastructure\nminikube stop       # stop the cluster (preserves state)\n# minikube delete   # full teardown — removes all cluster data\n```\n\n---\n\n### Frontend\n\nFrontend dev servers are independent of Docker vs. Kubernetes — start them the same way either way.\n\n```bash\n# Install npm dependencies (once after cloning, or after package changes)\nmake frontend-install\n\n# Start both frontend dev servers (web-user: 3000, web-admin: 3001)\nmake frontend-start\n```\n\n`frontend-start` is equivalent to `frontend-install` + `frontend-dev`. Use `make frontend-dev` to skip the install step on subsequent runs.\n\n```bash\n# Stop frontend dev servers\nmake frontend-stop\n```\n\n---\n\n## Useful commands\n\n```bash\n# Tail all Docker Compose service logs\nmake logs\n\n# Inspect LocalStack S3 buckets\naws --endpoint-url=http://localhost:4566 s3 ls\n```\n\n---\n\n## API Gateway\n\n### Docker Compose (Option A) — Kong on port 8100\n\nAll browser/client API traffic goes through Kong:\n\n```\nhttp://localhost:8100/api/v1/auth/*        → user-service:8000\nhttp://localhost:8100/api/v1/users/*       → user-service:8000\nhttp://localhost:8100/api/v1/roles/*       → user-service:8000\nhttp://localhost:8100/api/v1/permissions/* → user-service:8000\nhttp://localhost:8100/api/v1/videos/*      → video-service:8001\nhttp://localhost:8100/api/v1/catalog/*     → video-service:8001\nhttp://localhost:8100/api/v1/stream/*      → streaming-service:3002\nhttp://localhost:8100/api/v1/billing/*     → billing-service:8003\n```\n\nKong admin API (inspect live config/routes): `http://localhost:8101`\n\nKong is configured in **DB-less declarative mode** — all config lives in `infrastructure/kong/kong.yml`. Reload after changes with `docker exec infrastructure-kong-1 kong reload`.\n\n### Kubernetes (Option B) — nginx Ingress on port 80\n\nThe same routes are served by the nginx Ingress controller at `http://$(minikube ip)`:\n\n```\nhttp://\u003cminikube-ip\u003e/api/v1/auth/*        → user-service:8000\nhttp://\u003cminikube-ip\u003e/api/v1/users/*       → user-service:8000\nhttp://\u003cminikube-ip\u003e/api/v1/roles/*       → user-service:8000\nhttp://\u003cminikube-ip\u003e/api/v1/permissions/* → user-service:8000\nhttp://\u003cminikube-ip\u003e/api/v1/videos/*      → video-service:8001\nhttp://\u003cminikube-ip\u003e/api/v1/catalog/*     → video-service:8001\nhttp://\u003cminikube-ip\u003e/api/v1/stream/*      → streaming-service:3002\nhttp://\u003cminikube-ip\u003e/api/v1/billing/*     → billing-service:8003\n```\n\n## Architecture\n\n| Service | Tech | DB | Description |\n|---|---|---|---|\n| auth-service | FastAPI | PostgreSQL + Redis | RS256 JWT issuance, login, token refresh |\n| user-service | Django REST | PostgreSQL + Redis | Profiles, RBAC, trial tracking |\n| video-service | Django REST | PostgreSQL + S3 | Multipart upload orchestration, video catalog, transcode trigger |\n| transcode-worker | Python + FFmpeg | S3 | Async video transcoding |\n| streaming-service | Fastify (Node.js) | Redis | HLS/DASH manifest serving |\n| live-service | Node.js + nginx-rtmp | Redis | RTMP ingest + live HLS |\n| social-service | FastAPI | MongoDB | Comments, likes, follows |\n| billing-service | FastAPI + SQLAlchemy | PostgreSQL | Stripe subscriptions, trials, invoices, in-process scheduled jobs (APScheduler) |\n| search-service | FastAPI | Elasticsearch | Full-text video/channel search |\n| notification-service | FastAPI | — | Email, push, in-app notifications |\n| ai-service | FastAPI + Claude API | PostgreSQL + Redis | AI-powered features |\n| web-user | Next.js | — | Viewer-facing app (port 3000) |\n| web-admin | Next.js | — | Admin dashboard (port 3001) |\n\n## Local Infrastructure\n\n| Service | Port | Notes |\n|---|---|---|\n| PostgreSQL | 5432 | |\n| MongoDB | 27017 | |\n| Redis | 6379 | |\n| Elasticsearch | 9200 | Log storage, APM data |\n| Kafka | 9092 | |\n| LocalStack (AWS) | 4566 | S3, SQS, SNS, SSM, Secrets Manager |\n| OTel Collector | 4317 / 4318 | gRPC / HTTP — services send traces here |\n| APM Server | 8200 | Elastic APM — services send APM data here |\n| Fluent Bit | 24224 | Centralized log collector (fluentd protocol) |\n| Redis exporter | 9121 | Redis metrics for Prometheus |\n| Kafka exporter | 9308 | Kafka consumer-lag metrics for Prometheus |\n| **Kong proxy** | **8100** | **All API traffic goes here** |\n| Kong admin | 8101 | Status, route inspection |\n| user-service | 8000 | Direct access (bypasses Kong) |\n| video-service | 8001 | Direct access (bypasses Kong) |\n| streaming-service | 3002 | Direct access (bypasses Kong) |\n| billing-service | 8003 | Direct access (bypasses Kong) |\n\n## Local UIs\n\nAll browser-accessible endpoints in one place:\n\n| UI | URL | Credentials | Description |\n|---|---|---|---|\n| **web-user** | `http://localhost:3000` | — | Viewer-facing frontend |\n| **web-admin** | `http://localhost:3001` | — | Admin dashboard frontend |\n| **Kafka UI** | `http://localhost:8080` | — | Browse topics, messages, consumer groups |\n| **Jaeger** | `http://localhost:16686` | — | Distributed traces across services |\n| **Grafana** | `http://localhost:3003` | `admin` / `admin` | Noxrel Overview dashboard (auto-provisioned) |\n| **Kibana → Discover** | `http://localhost:5601` | `elastic` / `noxrel_dev` | Structured JSON logs, filterable by `trace_id` / `service` |\n| **Kibana → APM** | `http://localhost:5601/app/apm` | `elastic` / `noxrel_dev` | Latency, error rate, throughput per service |\n| **Prometheus** | `http://localhost:9090` | — | Raw metrics, ad-hoc PromQL queries |\n| **Elasticsearch** | `http://localhost:9200` | `elastic` / `noxrel_dev` | Raw ES API (log/APM indices) |\n| **Kong admin** | `http://localhost:8101` | — | Inspect live routes and Kong config |\n| **user-service Django admin** | `http://localhost:8000/admin/` | `admin@admin.com` / `admin1234` | Manage users, roles, permissions |\n| **video-service Django admin** | `http://localhost:8001/admin/` | *(create via manage.py)* | Manage video metadata |\n\n\u003e **user-service admin credentials** are set by `DEV_ADMIN_EMAIL`, `DEV_ADMIN_USERNAME`, and `DEV_ADMIN_PASSWORD` in `services/user-service/.env`. Defaults: email `admin@admin.com`, password `admin1234`. The account is created automatically on first boot, or manually with:\n\u003e ```bash\n\u003e docker exec -it infrastructure-user-service-1 uv run python manage.py create_dev_admin\n\u003e ```\n\n\u003e **Elasticsearch / Kibana credentials** — Elasticsearch security is enabled so Kibana can install the Fleet APM integration (required for the APM service inventory to populate). The dev-only superuser is `elastic` / `noxrel_dev`; Kibana authenticates internally as `kibana_system` / `noxrel_dev`. The password is sourced from `ELASTIC_PASSWORD` in `infrastructure/.env` (copied from `infrastructure/.env.example`, see Quick Start step 2) — for local dev only; rotate via AWS Secrets Manager in production.\n\n## Observability\n\nAll four implemented services are instrumented with OpenTelemetry. Traces, logs, and metrics flow through a central collector.\n\n| What you see | Where |\n|---|---|\n| Distributed traces (follow a request across services) | Jaeger `http://localhost:16686` |\n| Noxrel health, video pipeline, streaming, infra metrics | Grafana `http://localhost:3003` (`admin` / `admin`) |\n| Structured JSON logs, filter by `trace_id` / `service` | Kibana Discover `http://localhost:5601` (`elastic` / `noxrel_dev`) |\n| Latency, error rate, throughput per service | Kibana APM `http://localhost:5601/app/apm` (`elastic` / `noxrel_dev`) |\n| Raw PromQL queries | Prometheus `http://localhost:9090` |\n\n## Screenshots\n\n### Frontends\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd align=\"center\" width=\"50%\"\u003e\u003cb\u003eUser App — Browse\u003c/b\u003e\u003cbr/\u003e\u003cimg src=\"images/user-home-page.png\" alt=\"User home page\"/\u003e\u003c/td\u003e\n\u003ctd align=\"center\" width=\"50%\"\u003e\u003cb\u003eUser App — Video Playback\u003c/b\u003e\u003cbr/\u003e\u003cimg src=\"images/user-ui-video-watching.png\" alt=\"Video watching\"/\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd align=\"center\" colspan=\"2\"\u003e\u003cb\u003eAdmin Dashboard\u003c/b\u003e\u003cbr/\u003e\u003cimg src=\"images/admin-dashboard.png\" alt=\"Admin dashboard\" width=\"75%\"/\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n### Observability\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd align=\"center\" width=\"50%\"\u003e\u003cb\u003eKibana APM — All Services\u003c/b\u003e\u003cbr/\u003e\u003cimg src=\"images/apm_services.png\" alt=\"APM services inventory\"/\u003e\u003c/td\u003e\n\u003ctd align=\"center\" width=\"50%\"\u003e\u003cb\u003eKibana APM — user-service Detail\u003c/b\u003e\u003cbr/\u003e\u003cimg src=\"images/apm_user_service.png\" alt=\"APM user-service\"/\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd align=\"center\" width=\"50%\"\u003e\u003cb\u003eGrafana — Noxrel Overview\u003c/b\u003e\u003cbr/\u003e\u003cimg src=\"images/grafana.png\" alt=\"Grafana platform overview\"/\u003e\u003c/td\u003e\n\u003ctd align=\"center\" width=\"50%\"\u003e\u003cb\u003eKafka UI — Topics\u003c/b\u003e\u003cbr/\u003e\u003cimg src=\"images/apache_kafka.png\" alt=\"Apache Kafka topics\"/\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n---\n\n## Git Workflow\n\n### Commit message format\n\nAll commits must follow [Conventional Commits](https://www.conventionalcommits.org/). The `commit-msg` hook enforces this automatically.\n\n```\n\u003ctype\u003e(\u003cscope\u003e): \u003cdescription\u003e\n\nfeat(user-service): add email verification endpoint\nfix(auth-service): refresh token expiry off by one\ndocs: update local dev setup in README\nci: add billing-service to test matrix\n```\n\n**Allowed types:** `feat` `fix` `docs` `style` `refactor` `perf` `test` `build` `ci` `chore` `revert`\n\n**Breaking change:** append `!` before the colon — `feat(auth)!: drop v1 token support`\n\n### What the hooks check on every commit\n\n| Hook | What it does |\n|---|---|\n| `trailing-whitespace` | Strips trailing whitespace |\n| `end-of-file-fixer` | Ensures files end with a newline |\n| `check-yaml` / `check-json` / `check-toml` | Validates config file syntax |\n| `check-merge-conflict` | Blocks accidental merge conflict markers |\n| `detect-private-key` | Blocks accidental secret commits |\n| `check-added-large-files` | Blocks files \u003e 1 MB |\n| `ruff` (with `--fix`) | Lints Python, auto-fixes what it can |\n| `ruff-format` | Formats Python code |\n| `mypy` | Type-checks all services |\n| `commitizen` | Validates conventional commit format |\n\n### Run all checks manually (without committing)\n\n```bash\nmake lint\n```\n\n### Bypass hooks in an emergency\n\n```bash\ngit commit --no-verify -m \"chore: emergency hotfix\"\n```\n\nUse sparingly — CI runs the same checks and will catch any bypass.\n\n## Services\n\n| Service | Status | README |\n|---|---|---|\n| user-service | ✅ implemented | [services/user-service/README.md](services/user-service/README.md) |\n| video-service | ✅ implemented | [services/video-service/README.md](services/video-service/README.md) |\n| transcode-worker | ✅ implemented | [services/transcode-worker/README.md](services/transcode-worker/README.md) |\n| streaming-service | ✅ implemented | [services/streaming-service/README.md](services/streaming-service/README.md) |\n| web-admin | ✅ implemented | [frontend/web-admin/README.md](frontend/web-admin/README.md) |\n| live-service | pending | — |\n| social-service | pending | — |\n| billing-service | ✅ implemented | - |\n| search-service | pending | — |\n| notification-service | pending | — |\n| ai-service | pending | — |\n| web-user | ✅ implemented | [frontend/web-user/README.md](frontend/web-user/README.md) |\n\n## Repository Layout\n\n```\nservices/          # Backend microservices\nfrontend/          # Next.js apps (web-user, web-admin)\ninfrastructure/    # Docker Compose, Terraform, LocalStack init scripts\ndocs/              # Architecture docs and per-phase implementation guides\n.github/workflows/ # CI: lint + test matrix per service\n```\n\n## Key Conventions\n\n- Every service exposes a `/health` endpoint.\n- All logging is structured JSON: `{ts, level, service, message, trace_id, request_id}`.\n- Config via environment variables — never commit secrets.\n- DB migrations: Alembic (FastAPI), Django migrations (Django).\n- Cross-service async communication via Kafka; every consumer has a DLQ.\n\n## License\n\nMIT License — see [LICENSE](LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobaidullah-faruk%2Fnoxrel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fobaidullah-faruk%2Fnoxrel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobaidullah-faruk%2Fnoxrel/lists"}