# Ikar

Ikar is a working Telegram-like messenger MVP built in this folder with an ASP.NET Core backend, an Android MAUI client, and a dedicated WinUI 3 desktop client for Windows.

## Stack

- `src/Ikar.Server`: ASP.NET Core 10 Web API + SignalR + SQLite.
- `src/Ikar.Shared`: shared DTO/contracts used by server and client.
- `src/Ikar.Client`: .NET MAUI client targeting `net10.0-android`.
- `src/Ikar.WinUI`: WinUI 3 desktop client targeting `net10.0-windows10.0.19041.0`.

## Implemented

- phone-based sign-in with one-time test codes
- JWT access tokens with rotating refresh tokens
- secure client-side session persistence
- seeded demo accounts and sample chats
- Android contact-book lookup against registered phone numbers
- direct chats
- group chats
- channels with owner-only publishing
- message history persisted in SQLite
- file attachments with metadata persisted in SQLite
- voice notes as audio-classified attachments
- inline voice note playback in Android chat
- message edit/delete with realtime updates
- authenticated attachment download
- real-time message delivery with SignalR
- Android direct-chat audio/video calls with SignalR-based signaling and WebRTC media transport
- dedicated Android call screen for incoming and outgoing audio calls
- Android call ring/ringback tones and proximity-based screen blanking during active calls
- Android push registration API and FCM-based push delivery pipeline
- web admin panel for users, chats, attachments, and system status
- public landing page at `/` with latest Android APK download
- publishable Windows `exe`
- publishable Android `apk`

## Not Implemented

This is not full Telegram parity. The following are not implemented in this MVP:

- microphone recording UI for voice notes
- stickers and inline media previews
- end-to-end encryption
- advanced channel admin roles, granular moderation policies, multi-device sync edge cases
- Windows cloud push notifications

## Demo Accounts

- `alice`, phone `+10000000001`
- `bob`, phone `+10000000002`
- `carol`, phone `+10000000003`

These are created automatically on first server start in `src/Ikar.Server/Data/ikar.db`.

## Run

1. Start the backend:

```powershell
./scripts/run-server.ps1
```

2. Run the Windows client from Visual Studio or CLI:

```powershell
dotnet build .\src\Ikar.WinUI\Ikar.WinUI.csproj -f net10.0-windows10.0.19041.0 -p:Platform=x64
.\src\Ikar.WinUI\bin\x64\Debug\net10.0-windows10.0.19041.0\win-x64\Ikar.WinUI.exe
```

3. Run the Android client from Visual Studio or install the generated APK:

```powershell
dotnet build .\src\Ikar.Client\Ikar.Client.csproj -f net10.0-android
```

The Android client is hard-wired to the current production endpoint:

- Android client default base URL: `https://ikar.kusoft.xyz`

The Android login screen no longer exposes a server URL field.

The production Docker deployment binds the backend to `127.0.0.1:5099` on the Raspberry Pi host. Public and LAN clients should use the reverse-proxied HTTPS endpoint instead of direct access to port `5099`:

- public Android / browser / admin URL: `https://ikar.kusoft.xyz`

## Run In Docker

Docker assets for Raspberry Pi 5 are included in the repo:

- `Dockerfile.server`
- `docker-compose.rpi5.yml`
- `.env.example`
- `.env.server`
- `docs/raspberry-pi-5-docker.md`

Quick start:

```bash
cp .env.example .env
docker compose -f docker-compose.rpi5.yml up -d --build
```

To place server data on an external disk, set `IKAR_DATA_PATH` before running Compose. The current production Pi uses `/media/myDrive/ikar-data`.

Health check:

```bash
curl http://127.0.0.1:5099/health
```

Admin panel:

- `/admin` redirects to `/admin/index.html`
- configure admin login through `Admin__Username` and `Admin__Password`
- the admin surface uses its own cookie session and does not reuse messenger JWT tokens
- `/` serves a public landing page with a button for the latest Android APK
- `/api/app-updates/android/download/latest` returns the latest published Android APK
- `scripts/publish-android-update.ps1` keeps only the newest `3` Android APK files on the server by default; override with `-KeepLatestCount <N>` if needed

## Publish

Windows EXE:

```powershell
./scripts/publish-windows.ps1
```

Android APK:

```powershell
./scripts/publish-android-apk.ps1
```

Smoke test against the running backend:

```powershell
./scripts/smoke-test.ps1
```

## Published Artifacts

- Windows EXE: `artifacts/winui-win64/Ikar.WinUI.exe`
- Android APK: `src/Ikar.Client/bin/Release/net10.0-android/publish/com.seven.ikar.apk`
- Signed Android APK: `src/Ikar.Client/bin/Release/net10.0-android/publish/com.seven.ikar-Signed.apk`

## Solutions

- XML solution: `Ikar.slnx`
- classic Visual Studio solution: `IkarClassic.sln`

## Auth Notes

- Android client auth is currently `phone-only`.
- The current verification flow returns a temporary test code from the server and shows it in the client UI.
- Real SMS delivery is not implemented yet; it can be connected later by adding an SMS provider.
- Access tokens are short-lived JWTs.
- Refresh tokens are rotated on refresh and stored as hashed server-side sessions.
- Logout revokes the active session immediately, and the revoked access token stops working because the server validates the session id (`sid`) on authenticated requests.
- Session `LastSeenAt` writes are throttled server-side to reduce unnecessary SQLite writes on every authenticated request.
- The JWT signing key in `src/Ikar.Server/appsettings.json` is a development default. Replace it with a strong secret via configuration before any real deployment.

## Contacts Notes

- Android contact discovery requests `READ_CONTACTS` and only shows people from the device phone book.
- Server-side contact resolution matches users by normalized phone number through `api/users/contacts/resolve`.
- Users without a registered phone number do not appear in the Android contacts screen.

## Attachment Notes

- Attachments are stored on the server under `src/Ikar.Server/Data/Attachments`.
- Metadata stored for each attachment: original file name, content type, file size, upload time, and owning message.
- Audio files are classified as `voice notes` by MIME type / file extension and use the same secure attachment pipeline.
- Attachment content is downloaded through an authenticated API route, not via a public static files directory.

## Channel And Message Lifecycle Notes

- Channels are broadcast rooms: the creator is the owner, selected users join as subscribers, and only owners can publish.
- Message edit/delete is synchronized through SignalR `MessageUpdated` events.
- Delete is implemented as a soft-delete on the message row with attachment cleanup on the server.

## Call Notes

- Calls are currently implemented only for Android and only in direct one-to-one chats.
- The current call architecture uses SignalR for call lifecycle/signaling and WebRTC for audio/video media transport.
- ICE servers are configured server-side through the `WebRtc` configuration section.
- WinUI does not support calls in the current build.

## Push Setup Notes

- Android push in this repo is implemented through Firebase Cloud Messaging HTTP v1 on the server and `FirebaseMessagingService` on the Android client.
- Server-side device registrations are stored in SQLite in the `PushDevices` table and managed through `api/push/devices`.
- Push dispatch is queued through a background worker so message sends do not wait on outbound Firebase network latency.
- To enable real Android delivery, set `Push:FirebaseProjectId` and either `Push:ServiceAccountJsonPath` or `Push:ServiceAccountJson` in `src/Ikar.Server/appsettings.json` or environment-specific configuration.
- Before building the Android APK, fill `src/Ikar.Client/Resources/Raw/firebase.android.json` with the Android Firebase values: `applicationId`, `projectId`, `apiKey`, `senderId`.
- If the Firebase configuration is left empty, the app still builds and the server still accepts device registrations, but actual FCM delivery is skipped.
- The current Windows WinUI desktop target is unpackaged (`WindowsPackageType=None`), so this pass does not implement Windows cloud push.

## Server Runtime Notes

- Chat list loading now fetches only the latest message per chat for summary rendering instead of loading full message history.
- The server exposes `/health` for container and deployment health checks.
- The server exposes `/admin/api/*` plus static admin assets under `/admin/*` when admin credentials are configured.
- Startup enables SQLite write-ahead logging and ensures an index on `ChatMembers.UserId` for chat list lookup.
