major: kestrel is now a tak server #6
24
README.md
24
README.md
@@ -36,7 +36,7 @@ Camera and geolocation in the browser require a **secure context** (HTTPS) when
|
||||
npm run dev
|
||||
```
|
||||
|
||||
3. On your phone, open **https://192.168.1.123:3000** (same IP you passed above). Accept the browser's “untrusted certificate” warning once (e.g. Advanced → Proceed). Then log in and use Share live; camera and location will work.
|
||||
3. On your phone, open **https://192.168.1.123:3000** (same IP you passed above). Accept the browser's "untrusted certificate" warning once (e.g. Advanced → Proceed). Then log in and use Share live; camera and location will work.
|
||||
|
||||
Without the certs, `npm run dev` still runs over HTTP as before.
|
||||
|
||||
@@ -50,7 +50,7 @@ The **Share live** feature uses WebRTC for real-time video streaming from mobile
|
||||
- **Mediasoup** server (runs automatically in the Nuxt process)
|
||||
- **mediasoup-client** (browser library, included automatically)
|
||||
|
||||
**Streaming from a phone on your LAN:** The server auto-detects your machine's LAN IP (from network interfaces) and uses it for WebRTC. Open **https://<your-LAN-IP>:3000** on both phone and laptop (same IP as for your dev cert). To override (e.g. Docker or multiple NICs), set `MEDIASOUP_ANNOUNCED_IP`. Ensure firewall allows UDP/TCP ports 40000–49999 on the server.
|
||||
**Streaming from a phone on your LAN:** The server auto-detects your machine's LAN IP (from network interfaces) and uses it for WebRTC. Open **https://<your-LAN-IP>:3000** on both phone and laptop (same IP as for your dev cert). To override (e.g. Docker or multiple NICs), set `MEDIASOUP_ANNOUNCED_IP`. Ensure firewall allows UDP/TCP ports 40000-49999 on the server.
|
||||
|
||||
See [docs/live-streaming.md](docs/live-streaming.md) for setup and usage.
|
||||
|
||||
@@ -60,12 +60,12 @@ KestrelOS can act as a **TAK Server** so ATAK and iTAK devices connect and share
|
||||
|
||||
## Scripts
|
||||
|
||||
- `npm run dev` – development server
|
||||
- `npm run build` – production build
|
||||
- `npm run test` – run tests
|
||||
- `npm run test:coverage` – run tests with coverage (85% threshold)
|
||||
- `npm run test:e2e` – Playwright E2E tests
|
||||
- `npm run lint` – ESLint (zero warnings)
|
||||
- `npm run dev` - development server
|
||||
- `npm run build` - production build
|
||||
- `npm run test` - run tests
|
||||
- `npm run test:coverage` - run tests with coverage (85% threshold)
|
||||
- `npm run test:e2e` - Playwright E2E tests
|
||||
- `npm run lint` - ESLint (zero warnings)
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -76,7 +76,7 @@ Full docs are in the **[docs/](docs/README.md)** directory: [installation](docs/
|
||||
- **Devices**: Manage cameras/devices via the API (`/api/devices`); see [Map and cameras](docs/map-and-cameras.md). Each device needs `name`, `device_type`, `lat`, `lng`, `stream_url`, and `source_type` (`mjpeg` or `hls`).
|
||||
- **Environment**: No required env vars for basic run. For production, set `HOST=0.0.0.0` and expose ports 3000 (web/API) and 8089 (CoT). Set `COT_TTL_MS=90000`, `COT_REQUIRE_AUTH=true`. For TLS use `.dev-certs/` or set `COT_SSL_CERT` and `COT_SSL_KEY`.
|
||||
- **Authentication**: The login page always offers password sign-in (local). Optionally set `BOOTSTRAP_EMAIL` and `BOOTSTRAP_PASSWORD` before the first run to create the first admin; otherwise a default admin is created and its credentials are printed in the terminal. To also show an OIDC sign-in button, configure `OIDC_ISSUER`, `OIDC_CLIENT_ID`, `OIDC_CLIENT_SECRET`, and optionally `OIDC_LABEL`, `OIDC_REDIRECT_URI`. See [docs/auth.md](docs/auth.md) for local login, OIDC config, and sign up.
|
||||
- **Bootstrap admin** (when using local auth): The server initializes the database and runs bootstrap at startup. On first run (no users in the database), it creates the first admin. If you set `BOOTSTRAP_EMAIL` and `BOOTSTRAP_PASSWORD` before starting, that account is created. If you don't set them, a default admin is created (identifier: `admin`) with a random password and the credentials are printed in the terminal—copy them and sign in at `/login`, then change the password or add users via Members. Use **Members** to change roles (admin, leader, member). Only admins can change roles; admins and leaders can edit POIs.
|
||||
- **Bootstrap admin** (when using local auth): The server initializes the database and runs bootstrap at startup. On first run (no users in the database), it creates the first admin. If you set `BOOTSTRAP_EMAIL` and `BOOTSTRAP_PASSWORD` before starting, that account is created. If you don't set them, a default admin is created (identifier: `admin`) with a random password and the credentials are printed in the terminal-copy them and sign in at `/login`, then change the password or add users via Members. Use **Members** to change roles (admin, leader, member). Only admins can change roles; admins and leaders can edit POIs.
|
||||
- **Database**: SQLite file at `data/kestrelos.db` (created automatically). Contains users, sessions, and POIs.
|
||||
|
||||
## Docker
|
||||
@@ -106,9 +106,9 @@ Health: `GET /health` (overview), `GET /health/live` (liveness), `GET /health/re
|
||||
|
||||
Merges to `main` trigger a semver release. Use one of these prefixes in your PR title to set the version bump:
|
||||
|
||||
- `major:` – breaking changes
|
||||
- `minor:` – new features
|
||||
- `patch:` – bug fixes, docs (default if no prefix)
|
||||
- `major:` - breaking changes
|
||||
- `minor:` - new features
|
||||
- `patch:` - bug fixes, docs (default if no prefix)
|
||||
|
||||
Example: `minor: Add map layer toggle`
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ function getPoiIcon(L, poi) {
|
||||
})
|
||||
}
|
||||
|
||||
const LIVE_ICON_COLOR = '#22c9c9' /* kestrel-accent – JS string for Leaflet SVG */
|
||||
const LIVE_ICON_COLOR = '#22c9c9' /* kestrel-accent - JS string for Leaflet SVG */
|
||||
function getLiveSessionIcon(L) {
|
||||
const html = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="${LIVE_ICON_COLOR}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="5"/><circle cx="12" cy="12" r="2" fill="${LIVE_ICON_COLOR}"/></svg>`
|
||||
return L.divIcon({
|
||||
@@ -141,7 +141,7 @@ function getLiveSessionIcon(L) {
|
||||
})
|
||||
}
|
||||
|
||||
const COT_ICON_COLOR = '#f59e0b' /* amber – ATAK/CoT devices */
|
||||
const COT_ICON_COLOR = '#f59e0b' /* amber - ATAK/CoT devices */
|
||||
function getCotEntityIcon(L) {
|
||||
const html = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="${COT_ICON_COLOR}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><circle cx="12" cy="8" r="2.5" fill="${COT_ICON_COLOR}"/></svg>`
|
||||
return L.divIcon({
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
Wrong host: server sees <strong>{{ failureReason.wrongHost.serverHostname }}</strong> but you opened this page at <strong>{{ failureReason.wrongHost.clientHostname }}</strong>. Use the same URL or set MEDIASOUP_ANNOUNCED_IP.
|
||||
</p>
|
||||
<ul class="normal-case list-inside list-disc text-left text-kestrel-muted">
|
||||
<li><strong>Firewall:</strong> Open UDP/TCP 40000–49999 on the server.</li>
|
||||
<li><strong>Firewall:</strong> Open UDP/TCP 40000-49999 on the server.</li>
|
||||
<li><strong>Wrong host:</strong> Server must see the same address you use.</li>
|
||||
<li><strong>Restrictive NAT / cellular:</strong> TURN may be required.</li>
|
||||
</ul>
|
||||
@@ -66,7 +66,7 @@
|
||||
Wrong host: server sees <strong>{{ failureReason.wrongHost.serverHostname }}</strong> but you opened at <strong>{{ failureReason.wrongHost.clientHostname }}</strong>.
|
||||
</p>
|
||||
<ul class="normal-case list-inside list-disc text-left text-kestrel-muted">
|
||||
<li>Firewall: open ports 40000–49999.</li>
|
||||
<li>Firewall: open ports 40000-49999.</li>
|
||||
<li>Wrong host: use same URL or set MEDIASOUP_ANNOUNCED_IP.</li>
|
||||
<li>Restrictive NAT: TURN may be required.</li>
|
||||
</ul>
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
class="border-b border-kestrel-border"
|
||||
>
|
||||
<td class="px-4 py-2 text-kestrel-text">
|
||||
{{ p.label || '—' }}
|
||||
{{ p.label || '-' }}
|
||||
</td>
|
||||
<td class="px-4 py-2 text-kestrel-muted">
|
||||
{{ p.lat }}
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
</h3>
|
||||
<div class="kestrel-card p-4">
|
||||
<p class="mb-3 text-sm text-kestrel-text">
|
||||
Scan this QR code with iTAK (or ATAK) to add this KestrelOS server. You’ll be prompted for your KestrelOS username and password after scanning.
|
||||
Scan this QR code with iTAK (or ATAK) to add this KestrelOS server. You'll be prompted for your KestrelOS username and password after scanning.
|
||||
</p>
|
||||
<div
|
||||
v-if="takQrDataUrl"
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
Wrong host: server sees <strong>{{ webrtcFailureReason.wrongHost.serverHostname }}</strong> but you opened this page at <strong>{{ webrtcFailureReason.wrongHost.clientHostname }}</strong>. Use the same URL on phone and server, or set MEDIASOUP_ANNOUNCED_IP.
|
||||
</p>
|
||||
<ul class="mt-2 list-inside list-disc space-y-0.5 text-kestrel-muted">
|
||||
<li><strong>Firewall:</strong> Open UDP/TCP ports 40000–49999 on the server.</li>
|
||||
<li><strong>Firewall:</strong> Open UDP/TCP ports 40000-49999 on the server.</li>
|
||||
<li><strong>Wrong host:</strong> Server must see the same address you use (see above or open /api/live/debug-request-host).</li>
|
||||
<li><strong>Restrictive NAT / cellular:</strong> A TURN server may be required (future enhancement).</li>
|
||||
</ul>
|
||||
@@ -68,7 +68,7 @@
|
||||
v-if="sharing"
|
||||
class="absolute bottom-2 left-2 rounded bg-black/70 px-2 py-1 text-xs text-green-400"
|
||||
>
|
||||
● Live — you appear on the map
|
||||
● Live - you appear on the map
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -273,7 +273,7 @@ async function startSharing() {
|
||||
return
|
||||
}
|
||||
|
||||
// 5. Get location (continuous) — also requires HTTPS on mobile Safari
|
||||
// 5. Get location (continuous) - also requires HTTPS on mobile Safari
|
||||
if (!navigator.geolocation) {
|
||||
setError('Geolocation not supported in this browser.')
|
||||
cleanup()
|
||||
|
||||
@@ -4,8 +4,8 @@ Tactical Operations Center (TOC) for OSINT feeds: map view, cameras/devices, liv
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. [Installation](installation.md) — npm, Docker, or Helm
|
||||
2. [Authentication](auth.md) — First login (bootstrap admin or OIDC)
|
||||
3. [Map and cameras](map-and-cameras.md) — Add devices and view streams
|
||||
4. [ATAK and iTAK](atak-itak.md) — Connect TAK clients (port 8089)
|
||||
5. [Share live](live-streaming.md) — Stream from mobile device (HTTPS required)
|
||||
1. [Installation](installation.md) - npm, Docker, or Helm
|
||||
2. [Authentication](auth.md) - First login (bootstrap admin or OIDC)
|
||||
3. [Map and cameras](map-and-cameras.md) - Add devices and view streams
|
||||
4. [ATAK and iTAK](atak-itak.md) - Connect TAK clients (port 8089)
|
||||
5. [Share live](live-streaming.md) - Stream from mobile device (HTTPS required)
|
||||
|
||||
@@ -21,11 +21,11 @@ KestrelOS acts as a **TAK Server**. ATAK (Android) and iTAK (iOS) connect on **p
|
||||
|
||||
## iTAK (iOS)
|
||||
|
||||
**Option A — QR code (easiest):**
|
||||
**Option A - QR code (easiest):**
|
||||
1. KestrelOS **Settings** → **TAK Server** → Scan QR with iTAK
|
||||
2. Enter username/password when prompted
|
||||
|
||||
**Option B — Manual:**
|
||||
**Option B - Manual:**
|
||||
1. **Settings** → **Network** → Add **TAK Server**
|
||||
2. Set **Host**, **Port** (`8089`), enable SSL if needed
|
||||
3. Enable **Use Authentication**, enter username/password
|
||||
|
||||
@@ -32,8 +32,8 @@ OIDC users don't have a KestrelOS password. To use ATAK/iTAK:
|
||||
|
||||
## Roles
|
||||
|
||||
- **Admin** — Manage users, edit POIs, add/edit devices (API)
|
||||
- **Leader** — Edit POIs, add/edit devices (API)
|
||||
- **Member** — View map/cameras/POIs, use Share live
|
||||
- **Admin** - Manage users, edit POIs, add/edit devices (API)
|
||||
- **Leader** - Edit POIs, add/edit devices (API)
|
||||
- **Member** - View map/cameras/POIs, use Share live
|
||||
|
||||
Only admins can change roles (Members page).
|
||||
|
||||
@@ -15,7 +15,7 @@ Stream your phone's camera and location to KestrelOS. Appears as a **live sessio
|
||||
|
||||
- **HTTPS** (browsers require secure context for camera/geolocation)
|
||||
- **Camera and location permissions**
|
||||
- **WebRTC ports:** UDP/TCP `40000–49999` open on server
|
||||
- **WebRTC ports:** UDP/TCP `40000-49999` open on server
|
||||
|
||||
## Local Development
|
||||
|
||||
@@ -40,5 +40,5 @@ npm run dev
|
||||
|-------|-----|
|
||||
| "HTTPS required" | Use `https://` (not `http://`) |
|
||||
| "Media devices not available" | Ensure HTTPS and browser permissions |
|
||||
| "WebRTC: failed" / "Wrong host" | Set `MEDIASOUP_ANNOUNCED_IP`, open firewall ports `40000–49999` |
|
||||
| "WebRTC: failed" / "Wrong host" | Set `MEDIASOUP_ANNOUNCED_IP`, open firewall ports `40000-49999` |
|
||||
| Stream not visible | Check server reachability and firewall |
|
||||
|
||||
@@ -4,16 +4,16 @@ KestrelOS shows a **map** with devices, POIs, live sessions (Share live), and AT
|
||||
|
||||
## Map Layers
|
||||
|
||||
- **Devices** — Fixed feeds (IPTV, ALPR, CCTV, NVR, etc.) added via API
|
||||
- **POIs** — Points of interest (admin/leader can edit)
|
||||
- **Live sessions** — Mobile devices streaming via Share live
|
||||
- **CoT (ATAK/iTAK)** — Amber markers for connected TAK devices (position only)
|
||||
- **Devices** - Fixed feeds (IPTV, ALPR, CCTV, NVR, etc.) added via API
|
||||
- **POIs** - Points of interest (admin/leader can edit)
|
||||
- **Live sessions** - Mobile devices streaming via Share live
|
||||
- **CoT (ATAK/iTAK)** - Amber markers for connected TAK devices (position only)
|
||||
|
||||
## Cameras
|
||||
|
||||
A **camera** is either:
|
||||
1. A **device** — Fixed feed with stream URL
|
||||
2. A **live session** — Mobile device streaming via Share live
|
||||
1. A **device** - Fixed feed with stream URL
|
||||
2. A **live session** - Mobile device streaming via Share live
|
||||
|
||||
View via map markers or **Cameras** page (sidebar).
|
||||
|
||||
|
||||
@@ -202,14 +202,14 @@ function startCotServers() {
|
||||
serverState.tlsServer = createTlsServer(tlsOpts, socket => setupSocket(socket, true))
|
||||
serverState.tlsServer.on('error', err => console.error('[cot] TLS server error:', err?.message))
|
||||
serverState.tlsServer.listen(port, '0.0.0.0', () => {
|
||||
console.log('[cot] CoT server listening on 0.0.0.0:' + port + ' (TLS) — use this port in ATAK/iTAK and enable SSL')
|
||||
console.log('[cot] CoT server listening on 0.0.0.0:' + port + ' (TLS) - use this port in ATAK/iTAK and enable SSL')
|
||||
})
|
||||
}
|
||||
else {
|
||||
serverState.tcpServer = createTcpServer(socket => setupSocket(socket, false))
|
||||
serverState.tcpServer.on('error', err => console.error('[cot] TCP server error:', err?.message))
|
||||
serverState.tcpServer.listen(port, '0.0.0.0', () => {
|
||||
console.log('[cot] CoT server listening on 0.0.0.0:' + port + ' (plain TCP) — use this port in ATAK/iTAK with SSL disabled')
|
||||
console.log('[cot] CoT server listening on 0.0.0.0:' + port + ' (plain TCP) - use this port in ATAK/iTAK with SSL disabled')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user