Easy-to-manage touch screens
무료 오픈소스 터치스크린으로 USB 설정 가이드, 드래그‑드롭 편집기, 로컬 제어를 제공하며 Home Assistant용으로 제작되었습니다
이집트 일상을 위한 10가지 AI 비서, 이집트 아랍어·영어 제공, 출처와 일반 안내(의료·법·재무 조언 제외)
Ten specialised AI assistants for everyday life in Egypt, in Egyptian Arabic and English. General guidance with sources, not medical, legal or financial advice.
라이프스타일Python · TypeScriptGitHub @AbdallahYehya1온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗A digital mental-wellness platform built for Egyptian Arabic speakers first.
The planned MVP includes a warm, culturally relevant Egyptian Arabic AI wellness companion, daily mood check-ins, journaling, guided evidence-based exercises, basic progress tracking, and approved psychoeducation content. A path to qualified human therapists is planned for later.
This is a wellness-support product, not a therapist. It must never diagnose, prescribe or recommend medication, claim clinical certainty, present itself as a licensed professional, replace emergency or professional care, or encourage dependence on the AI. For high-risk users, normal AI generation stops and a controlled escalation flow takes over. That flow's content is pending clinical and legal review (docs/OPEN_DECISIONS.md). The service is for adults (18+) only.
Current status: Milestone 4 (mood check-in), built on M3 (consent and profile) and M2 (Supabase auth). There is no AI companion yet. See docs/IMPLEMENTATION_STATUS.md.
The system is a modular monolith: no microservices and no multi-agent AI.
| Layer | Stack |
|---|---|
| Frontend | Next.js 16 (App Router), TypeScript (strict), Tailwind CSS 4, @supabase/ssr |
| Backend | Python 3.12, FastAPI, Pydantic, PyJWT, SQLAlchemy 2 + Alembic, psycopg 3 |
| Auth | Supabase Auth (email/password, required email confirmation) |
| Database | PostgreSQL 17: Supabase in the cloud, a local container for dev, test and CI. App tables live in schema app. |
| Planned | pgvector (approved knowledge only), Redis only when there is a real need |
| Ops | Docker, Docker Compose, GitHub Actions |
Backend modules live in backend/app/. core, common, auth, users, consent,
audit and mood contain code; the rest are boundaries only:
core auth users consent conversations
ai/{provider,language,prompts,evaluation}
safety knowledge memory mood wellness journal analytics audit common
Every future chat message must pass through this pipeline, in this order. There is never a
direct request → LLM path. M2 implemented authentication and authorisation (identity
from the verified token). M3 adds the consent check (ConsentedUser, which answers 403
until the current consent version is accepted).
request → authentication → authorization → consent/policy checks
→ safety assessment → safety decision → context retrieval → AI generation
→ output safety validation → response → audit/telemetry
Design documents: ARCHITECTURE · API_CONTRACT · DATA_MODEL · OPEN_DECISIONS
/sign-up ──(Server Action: supabase.auth.signUp)──▶ /auth/check-email
email link ──▶ Supabase verify ──▶ /auth/confirm?code=… ──(exchangeCodeForSession)──▶ /dashboard
/sign-in ──(Server Action: signInWithPassword)──▶ session cookies ──▶ /dashboard
/dashboard ──(server-side fetch, Bearer access token)──▶ FastAPI GET /me ──▶ verified identity
Sign out ──(Server Action: signOut)──▶ cookies cleared ──▶ /sign-in
proxy.ts (Next 16's replacement for middleware) refreshes the session on
every request with supabase.auth.getClaims(). It sends signed-out visitors from
/dashboard to /sign-in, and signed-in users away from the auth pages. The dashboard
checks again on the server. Passwords only ever go to Supabase; this project stores none.app/auth verifies Supabase access tokens: signature via the project's JWKS
(or the legacy HS256 secret if configured), expiry, issuer, audience, subject and role.
CurrentPrincipal is the reusable FastAPI dependency. GET /me returns only
token-derived identity. Every failure is a uniform 401; if Supabase isn't configured,
it fails closed with 503./sign-in ─▶ /forgot-password ──(resetPasswordForEmail)──▶ "If an account exists…" (same for every email)
email link ──▶ Supabase verify ──▶ /auth/confirm?code=… ──(recovery verified)──▶ /reset-password
/reset-password ──(updateUser({ password }))──▶ signOut({ scope: "global" }) ──▶ /sign-in?status=password-updated
getClaims()), giving the user id and Supabase session id.getUser()). getClaims() alone can't
see a revoked session, because an access token stays valid until it expires.aw_password_reset: httpOnly, SameSite=Lax, path
/reset-password, 10 minutes). It is HMAC-SHA256-signed with the server-only
RECOVERY_MARKER_SECRET and binds the user id, the session id, an expiry and a random
token id. Signatures are compared in constant time./auth/confirm issues the marker, and only after Supabase has verified a
recovery. That means either the PKCE verifier created by resetPasswordForEmail, or a
type=recovery token hash. The marker is bound to the session in the token Supabase just
returned, verified with getClaims(token). next=/reset-password alone grants nothing.signOut({ scope: "global" }). Supabase then
revokes the refresh tokens and sessions, so a replay with that session fails the
getUser() check. This relies on the Auth server rejecting revoked sessions
(session_not_found). It was tested against a mock and still needs a manual check
against the real project. If the sign-out call fails, the session stays valid until the
marker expires.updateUser directly with them, without using this
page. Supabase's own controls are what limit that: session lifetime, and the project
setting "Secure password change", which requires a recent sign-in. Enabling that
setting is a project decision, and it is not changed here.Development setup for recovery (and sign-up) links:
SITE_URL (default http://localhost:3000). The
PKCE verifier cookie belongs to the host where the email was requested; a link opened on
127.0.0.1 when SITE_URL is localhost (or the reverse) can't complete. In
development, /forgot-password shows a note when the hosts differ.http://localhost to HTTPS ("localhost sent an invalid
response"), it has an HSTS entry for localhost. Either remove that entry yourself
(Edge/Chrome: edge://net-internals/#hsts → "Delete domain security policies" →
localhost), or use http://127.0.0.1:3000 with SITE_URL=http://127.0.0.1:3000, which
also needs that URL in the Supabase redirect allow-list.<SITE_URL>/auth/confirm?next=/reset-password (for example <SITE_URL>/**). If they
don't, Supabase sends the user to the Site URL instead and the reset can't finish.RECOVERY_MARKER_SECRET in frontend/.env.local (see frontend/.env.example). It is
read by the Next.js server only; never give it a NEXT_PUBLIC_ prefix or commit it.sign-in / email confirmation ─▶ /dashboard ─(GET /consents/me)─▶ status?
missing ─▶ /onboarding (optional display name, can be skipped)
─▶ /onboarding/consent (read, tick the box, "Agree and continue")
declined / outdated ─▶ /onboarding/consent
granted ─▶ dashboard
GET/PATCH /profile) is created on the first authenticated request and is
keyed only on the verified Supabase sub. There is no way to address another user's
profile.outdated until they
accept again.audit_events table, in the same transaction.consent accepted ─▶ /check-in (1–5 scale, "Skip for now") ─(POST /mood/checkins)─▶ /dashboard
/dashboard ─(GET /mood/history?limit=7)─▶ recent check-ins (date + score)
403 consent_required).[] until a vocabulary
is approved (OPEN_DECISIONS D-30). Notes are temporarily rejected server-side (any
note string → 422; omitted or null is fine) and the UI offers no note field, until
D-32 is decided.Deployment and configuration for every service: docs/DEPLOYMENT_RUNBOOK.md.
consent accepted ─▶ /chat (Adult · Prototype) ─(POST /chat/messages)─▶ signed reply + citations
CHAT_ENABLED=true and
CHAT_SAFETY_POLICY=synthetic-test-v0, then load the synthetic fixtures:
python -m app.knowledge.ingest app/knowledge/fixtures (from backend/).backend/ FastAPI app (app/), Alembic (alembic.ini, migrations/), tests/, Dockerfile
frontend/ Next.js app (app/, components/, lib/, proxy.ts), Vitest tests (*.test.ts[x])
docs/ Architecture, contracts, open decisions, implementation status
.github/ CI workflow
docker-compose.yml Local dev: Postgres 17, one-shot migrations, API
.env.example Server-only settings (backend / Compose)
Server-side and browser-side settings are kept in separate files:
| File | Read by | Contents |
|---|---|---|
.env (from .env.example) | Docker Compose | Server-only settings and secrets |
backend/.env (from .env.example) | Backend run directly | Same as above |
frontend/.env.local (from frontend/.env.example) | Next.js | NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY (public), plus SITE_URL and API_BASE_URL (server-side, not secret) |
To run authentication locally you need, from the Supabase dashboard (Project Settings → API):
sb_publishable_…).SUPABASE_URL (the same project URL). Add SUPABASE_JWT_SECRET only if the
project still uses the legacy shared JWT secret.Rules:
.env file. They are git-ignored, and CI fails if one is tracked.frontend/ or carry a NEXT_PUBLIC_
prefix. CI checks for both.SecretStr, so they are masked in repr, logs and
serialisation. Blank values count as unset.Authorization headers, passwords or emails. Auth rejections
log only a reason code, such as expired.pyproject.toml accepts 3.12–3.13)cd backend
python -m venv .venv
source .venv/bin/activate # Windows (Git Bash): source .venv/Scripts/activate
pip install -e ".[dev]"
cp ../.env.example .env # then set SUPABASE_URL
# Database: start the local Postgres, then apply migrations
docker compose up -d db # from the repository root
# in backend/.env: DATABASE_URL=postgresql://wellness:[email protected]:5432/wellness
alembic upgrade head
uvicorn app.main:app --reload # add --port 8001 if 8000 is taken
python -m pytest -q # starts a throwaway Postgres 17 via Docker (testcontainers),
# or uses TEST_DATABASE_URL if set; no real credentials needed
ruff check .
ruff format --check .
mypy app --show-error-codes # strict (CI also checks tests/)
GET /health returns exactly {"status": "ok"}.GET /me, /profile and /consents/me need Authorization: Bearer <Supabase access token>.
GET /consents/current is public.DATABASE_URL, profile and consent endpoints return 503 (fail closed).ENVIRONMENT=production.cd frontend
npm install
cp .env.example .env.local # then set the two NEXT_PUBLIC_SUPABASE_* values
npm run dev # http://localhost:3000
npm run lint
npm run typecheck
npm test # Vitest; Supabase is mocked
npm run build
If the API runs on a port other than 8000, set API_BASE_URL in frontend/.env.local.
app/globals.css defines semantic colours (lavender primary, sage
secondary, warm neutrals) for light and dark themes; every text/background pair was
checked for WCAG AA (≥ 4.5:1; control borders, focus and chart marks ≥ 3:1). Use the
token utilities (bg-surface, text-ink, border-line-strong, …), not raw colours.next/font/google — downloaded at build time and
served from this site (no runtime requests to Google).components/ui/primitives.tsx (buttons, cards, notices, badges,
24px checkbox), components/shell/ (app shell with skip link, desktop navigation
from 1024px and a bottom tab bar below it; public shell).aw_locale cookie (the
language switch), else the browser's Accept-Language (Arabic or English), else
Arabic. <html lang dir> is set per request. Copy lives in lib/i18n/messages.ts;
fixed server-action messages are translated at display time (lib/i18n/translate.ts,
with a test that every one has Arabic). The Arabic is provisional (D-19). Legal
consent text is shown in English until a reviewed translation exists.aw_theme cookie (device only).Africa/Cairo), in the visitor's language.npm run e2e runs the end-to-end, recovery-security and accessibility suite (axe,
WCAG 2.2 A/AA rules, 320–1440px, Arabic/English, light/dark) against a local stack with
a test-only mock auth server. See frontend/e2e/README.md. Not part of CI yet.
docker compose config # validate
docker compose up --build # API on http://127.0.0.1:8000
API_PORT=8001 docker compose up # if port 8000 is already taken
Compose runs Postgres, applies migrations once (migrate service), then starts the API.
It reads SUPABASE_URL from the root .env. The local database password is a
development-only value (LOCAL_DB_PASSWORD), and Postgres binds to 127.0.0.1.
.github/workflows/ci.yml runs on every pull request and on every push to main:
npm ci, lint, typecheck, Vitest, production build, on Node 22.env files; no server-only secret names in frontend/; no
secret-looking NEXT_PUBLIC_ variablesdocker compose config, image build, Postgres, migrations (must exit 0),
API start, /health returns exactly {"status":"ok"}, /consents/current is served,
and /me returns 401 anonymously and 503 without Supabase configuration/forgot-password?status=link-invalid.
Redirects stay on this site (safeNextPath). The "sent" message is identical whether or
not an account exists. Passwords, markers and tokens are never echoed or logged.