Join the Agent Deck Discord Server!
Discord에서 Agent Deck 커뮤니티 확인, 357명과 자유 음성·텍스트 채팅 이용
현지인과 여행자가 서로 도전하고, 도시에서 만나 증명합니다. 실시간 행사·모임·도시 채팅 제공, 회원가입 필요 없음
Locals challenge travelers, travelers challenge locals - take one on in any city and meet up to prove it. Plus live events, hangouts and city chat. No sign-up.
소셜PHP · TypeScriptGitHub @jaclette온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Locals open their city. Explorers experience it.
Hilads makes cities feel alive through people. Open the app, choose your mode - 🌍 Local or 🧭 Exploring - and jump into what's happening right now. No sign-up required.
Open app
→ geolocation resolves your city (or pick manually)
→ choose mode: 🌍 Local or 🧭 Exploring
→ Now screen: recurring spots + live events + active topic conversations
→ Locals: host a recurring spot, open your place, bring people to where you love
→ Explorers: discover what's happening, meet locals, feel the city
→ no account needed (Ghost mode)
→ register to unlock profile, DMs, friends, vibes, and notifications
Ghost session: instant access, persistent 32-char guestId, nickname + mode chosen on entry.
Registered account: full profile with mode identity, DMs, vibe system, friends, push notifications.
apps/
web/ React 18 + Vite - mobile-first PWA
mobile/ Expo SDK 52 + React Native (iOS + Android)
backend/
api/ PHP 8.2 REST API (Docker + Apache)
admin/ Internal backoffice (/admin - ops only)
src/ Repositories, services, push
scripts/ VAPID key generation, seed scripts
ws/ Node.js WebSocket server
docs/
mvp.md Full product definition
design.md UX and visual direction
infra/ Infrastructure notes
render.yaml Render service definitions
Backend - thin by design: router → repositories → PDO → PostgreSQL. No framework, no ORM.
WebSocket server - handles presence snapshots and real-time message push. PHP broadcasts via internal HTTP (fire-and-forget).
Feed DTO - GET /channels/{id}/now returns a single normalized { items, publicEvents } response. Events and topics share a consistent shape; web and mobile consume the same contract.
Push notifications - web push (VAPID) + native push via Expo Push API. Token registered at POST /api/v1/push/mobile-token.
Message pagination - initial load: 50 messages. Older messages fetched via before_id cursor (GET /channels/{id}/messages?before_id=<id>&limit=50).
Schema migrations - applied idempotently inside Database::pdo() on first connection. No migration runner.
Message retention - daily cron purges city chat (> today), topic chat (inactive > 24h), event chat (expired + 1h), DMs (> 7 days).
DATABASE_URL pointing to a remote instance)cd backend/api
cp .env.example .env
# fill: DATABASE_URL, R2_*, WS_INTERNAL_URL, MIGRATION_KEY
docker build -t hilads-api .
docker run -p 8080:80 --env-file .env hilads-api
API available at http://localhost:8080.
cd backend/ws
npm install
node server.js
# WS on :8081 (public connections), HTTP on :8082 (PHP broadcast calls)
cd apps/web
npm install
cp .env.example .env.local
# set VITE_API_URL=http://localhost:8080
# set VITE_WS_URL=ws://localhost:8081
npm run dev
# http://localhost:5173
cd apps/mobile
npm install
cp .env.example .env.local
# set EXPO_PUBLIC_API_URL=http://192.168.x.x:8080 ← use LAN IP, not localhost
# set EXPO_PUBLIC_WS_URL=ws://192.168.x.x:8081
npm run dev # Expo Go - fastest for iteration
npm run android # Android emulator or device
npm run ios # iOS simulator (Mac only)
The device/emulator cannot reach
localhost. Use your machine's LAN IP forEXPO_PUBLIC_API_URLandEXPO_PUBLIC_WS_URL.
Push notifications require a physical device and an EAS build.
npm install -g eas-cli
eas login
# Android APK (for testing, no store signing)
eas build --platform android --profile preview
# Android production (signed, for Play Store)
eas build --platform android --profile production
# iOS (requires Apple Developer account)
eas build --platform ios --profile production
Build profiles are defined in apps/mobile/eas.json.
backend/api/.env)| Variable | Description |
|---|---|
DATABASE_URL | PostgreSQL connection string |
R2_ACCOUNT_ID | Cloudflare R2 account ID |
R2_ACCESS_KEY_ID | R2 access key |
R2_SECRET_ACCESS_KEY | R2 secret |
R2_BUCKET | R2 bucket name |
R2_PUBLIC_URL | Public base URL for uploaded files |
WS_INTERNAL_URL | Internal URL of the WS server (e.g. http://ws:8082) |
WS_INTERNAL_TOKEN | Auth token for WS internal broadcast endpoint |
WS_ALLOWED_ORIGINS | Comma-separated CORS origins (e.g. https://hilads.live) |
MIGRATION_KEY | Secret for /internal/* endpoints |
POSTHOG_API_KEY | PostHog project API key (server-side capture) |
POSTHOG_HOST | PostHog ingest host |
SENTRY_DSN | Sentry DSN for hilads-backend |
APP_ENV | production / staging / development |
VAPID_PUBLIC_KEY | VAPID public key for web push |
VAPID_PRIVATE_KEY | VAPID private key for web push |
VAPID_SUBJECT | mailto: address or HTTPS URL |
GOOGLE_CLIENT_ID | Google OAuth client ID |
GOOGLE_CLIENT_SECRET | Google OAuth client secret |
ADMIN_USERNAME | Admin backoffice username |
ADMIN_PASSWORD | Admin backoffice password |
Generate VAPID keys:
php backend/api/scripts/generate-vapid-keys.php
apps/web/.env.local)| Variable | Description |
|---|---|
VITE_API_URL | Backend API base URL |
VITE_WS_URL | WebSocket server URL |
VITE_SENTRY_DSN | Sentry DSN for hilads-web |
apps/mobile/.env.local)| Variable | Description |
|---|---|
EXPO_PUBLIC_API_URL | Backend API base URL (no trailing slash) |
EXPO_PUBLIC_WS_URL | WebSocket server URL |
EXPO_PUBLIC_SENTRY_DSN | Sentry DSN for hilads-mobile |
EAS build env vars are defined in apps/mobile/eas.json under each profile's env block.
| Method | Path | Description |
|---|---|---|
POST | /api/v1/guest/session | Create a ghost session |
POST | /api/v1/auth/login | Registered user login |
POST | /api/v1/auth/register | Register account |
GET | /api/v1/auth/me | Current session info |
| Method | Path | Description |
|---|---|---|
POST | /api/v1/channels/{id}/join | Join a city channel |
GET | /api/v1/channels/{id}/messages | Fetch city chat messages (?before_id=&limit=) |
POST | /api/v1/channels/{id}/messages | Send a message |
GET | /api/v1/channels/{id}/now | Now feed: events + topics + public events |
GET | /api/v1/channels/{id}/events | List hilads events for a city |
GET | /api/v1/channels/{id}/city-events | List public (Ticketmaster) events |
GET | /api/v1/channels/{id}/events/upcoming | Upcoming events (next N days) |
| Method | Path | Description |
|---|---|---|
GET | /api/v1/channels/{id}/topics | List active topics for a city |
POST | /api/v1/channels/{id}/topics | Create a topic |
GET | /api/v1/topics/{id} | Get topic metadata |
GET | /api/v1/topics/{id}/messages | Fetch topic messages |
POST | /api/v1/topics/{id}/messages | Send a topic message |
POST | /api/v1/topics/{id}/mark-read | Mark topic as read |
| Method | Path | Description |
|---|---|---|
POST | /api/v1/channels/{id}/events | Create an event |
GET | /api/v1/events/{id} | Get event details |
PUT | /api/v1/events/{id} | Update event (creator only) |
DELETE | /api/v1/events/{id} | Delete event (creator only) |
GET | /api/v1/events/{id}/messages | Fetch event chat |
POST | /api/v1/events/{id}/messages | Send event chat message |
GET | /api/v1/events/{id}/participants | List participants |
POST | /api/v1/events/{id}/participants/toggle | Join / leave event |
| Method | Path | Description |
|---|---|---|
GET | /api/v1/users/{id} | Public profile |
GET | /api/v1/users/{id}/vibes | User's vibes |
POST | /api/v1/users/{id}/vibes | Leave a vibe |
GET | /api/v1/friends | Current user's friends list |
POST | /api/v1/friends/{id} | Add a friend |
GET | /api/v1/notifications | In-app notification feed |
POST | /api/v1/notifications/mark-read | Mark read |
GET | /api/v1/notification-preferences | Get push preferences |
PUT | /api/v1/notification-preferences | Update push preferences |
POST | /api/v1/push/subscribe | Register web push subscription |
POST | /api/v1/push/mobile-token | Register Expo push token |
DELETE | /api/v1/push/mobile-token | Unregister push token on logout |
All /internal/* routes require ?key=YOUR_MIGRATION_KEY or X-Api-Key header.
Returns 404 if MIGRATION_KEY is not configured, 403 on wrong key.
| Method | Path | Description |
|---|---|---|
POST | /internal/run-migrations | Idempotent DB migrations |
POST | /internal/cleanup | Purge stale messages + expired channels |
POST | /internal/event-series/generate | Generate recurring event occurrences |
POST | /internal/seed-static-venues | Import venue seed ({"dryRun": true/false}) |
POST | /internal/city-events/resync | Force-refresh Ticketmaster events for a city |
Two daily jobs, both protected by MIGRATION_KEY:
Generate recurring event occurrences - 2:00 AM UTC
POST /internal/event-series/generate?key=YOUR_KEY
Cleanup stale messages + expired channels - 3:00 AM UTC
POST /internal/cleanup?key=YOUR_KEY
Set up as Render Cron Jobs or via cron-job.org.
Bootstraps cities with recurring events so the Now screen is never empty on launch.
# Dry run first
curl -X POST "https://api.hilads.live/internal/seed-static-venues" \
-H "X-Api-Key: YOUR_MIGRATION_KEY" \
-d '{"dryRun": true}'
# Real import
curl -X POST "https://api.hilads.live/internal/seed-static-venues" \
-H "X-Api-Key: YOUR_MIGRATION_KEY" \
-d '{"dryRun": false}'
# Then generate today's occurrences
curl -X POST "https://api.hilads.live/internal/event-series/generate?key=YOUR_KEY"
Venue list: backend/api/src/venues_seed.php.
| Service | Platform | Config |
|---|---|---|
| PHP API | Render (Docker) | render.yaml - auto-deploy on push to main |
| WebSocket | Render (Node) | render.yaml - persistent process |
| Web frontend | Vercel | apps/web - auto-deploy on push to main |
| Native builds | EAS (Expo) | eas build --platform android/ios |
Production URLs:
https://api.hilads.livewss://ws.hilads.livehttps://hilads.liveAfter any API deployment, run migrations:
POST /internal/run-migrations?key=YOUR_KEY
Internal ops tool at /admin on the API service.
| Route | Description |
|---|---|
GET /admin | Dashboard - stats overview |
GET /admin/users | User list - searchable, read-only |
GET /admin/events | Event list - filterable by status |
GET /admin/events/{id}/edit | Edit event fields |
POST /admin/events/{id}/delete | Soft-delete an event |
Auth: env-var credentials (ADMIN_USERNAME + ADMIN_PASSWORD), PHP session, CSRF-protected.
apps/mobile/
app/
_layout.tsx Root layout, boot sequence, push token registration
(tabs)/
now.tsx Now feed - events + topics + filters
chat.tsx City chat (tab entry point)
cities.tsx City list + switcher
here.tsx Online users in current city
messages.tsx DMs + event chats
me.tsx Profile, My Events, friends
city-chat.tsx City chat (full-screen, no tabs)
notifications.tsx Notification preferences
notifications-history.tsx In-app notification feed
upcoming-events.tsx Full event calendar for a city
event/[id].tsx Event chat screen
topic/[id].tsx Topic chat screen
topic/create.tsx Create topic flow
event/create.tsx Create event flow (via city-chat)
user/[id].tsx Public profile screen
dm/[id].tsx Direct message screen
src/
api/ Typed REST API client
channels.ts Messages + channel join
events.ts Events + participants
topics.ts Topics + now feed
users.ts Profile + friends + vibes
notifications.ts Notification feed + preferences
uploads.ts Photo upload (Cloudflare R2)
services/
analytics.ts PostHog (disabled in __DEV__)
push.ts Expo push token registration
context/
AppContext.tsx Global state: identity, city, account
hooks/
useMessages.ts Shared message list + pagination + WS
features/
chat/ ChatMessage, ChatInput components
notifications/ NotificationHandler (foreground + deep-link)
types/index.ts Shared TypeScript types
constants.ts Colors, spacing, font sizes
PostHog tracks cross-platform behaviour. Every event includes a platform property.
posthog-js in apps/web/src/main.jsxposthog-react-native in apps/mobile/src/services/analytics.ts (disabled in __DEV__)AnalyticsService.php (fire-and-forget)| Project | DSN env var |
|---|---|
hilads-web | VITE_SENTRY_DSN |
hilads-backend | SENTRY_DSN |
hilads-mobile | EXPO_PUBLIC_SENTRY_DSN |
Sentry is skipped if the DSN is not set - safe for local dev.
| Area | Issue |
|---|---|
| Mobile iOS | Push notifications not fully validated end-to-end on TestFlight |
| My Events | Recurring events may appear as duplicates in the list |
| Topics | No WebSocket room join yet - uses 5s polling |
| City chat | Scroll-to-bottom on open inconsistent across platforms |
| Performance | Now feed endpoint not benchmarked; DB indexes on participant counts + topic queries not fully optimised |
Mode - Depth
Growth - Local Host Flywheel
hilads.live/event/[id]hilads.live/city/parisiOS Push Notifications
Topics - Real-time
Performance
docs/mvp.md - full product definition and architecture conceptsdocs/design.md - UX and visual directionCLAUDE.md - tech lead guidelines for AI agents