mirror of
git://f0xx.org/ac/ac-docs
synced 2026-07-29 08:38:53 +03:00
412 lines
15 KiB
Markdown
412 lines
15 KiB
Markdown
# Android Cast — exposed services catalog
|
||
|
||
<!-- doc-meta:start -->
|
||
| Field | Value |
|
||
|---|---|
|
||
| Author | Anton Afanasyeu |
|
||
| Revision | R1 |
|
||
| Creation date | 2026-06-10 |
|
||
| Last modification date | 2026-06-10 |
|
||
| Co-authored | |
|
||
| Severity | medium |
|
||
| State | in progress |
|
||
| Document type | technical |
|
||
<!-- doc-meta:end -->
|
||
|
||
\newpage
|
||
\newpage
|
||
|
||
---
|
||
|
||
|
||
_Validated: 2026-06-10 after FE `apps.conf` consolidation + BE sync. Public base: **https://apps.f0xx.org**._
|
||
|
||
**Documentation index:** [README.md](README.md) · Companion: [INFRA.md](INFRA.md), [20260608_BE_SERVICES_and_infra.md](20260608_BE_SERVICES_and_infra.md)
|
||
|
||
---
|
||
|
||
---
|
||
|
||
---
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
## Table of contents
|
||
|
||
<!-- toc -->
|
||
- [Validation snapshot](#validation-snapshot)
|
||
- [Hub landing](#hub-landing)
|
||
- [Console home](#console-home)
|
||
- [Issues (crash reports)](#issues-crash-reports)
|
||
- [Related endpoints](#related-endpoints)
|
||
- [Upload via curl](#upload-via-curl)
|
||
- [Bulk / synthetic issues](#bulk-synthetic-issues)
|
||
- [Tickets](#tickets)
|
||
- [Related endpoints](#related-endpoints-1)
|
||
- [Create via UI](#create-via-ui)
|
||
- [Create via curl (session cookie)](#create-via-curl-session-cookie)
|
||
- [Graphs](#graphs)
|
||
- [Related endpoints](#related-endpoints-2)
|
||
- [Fake session data (dev / BE)](#fake-session-data-dev-be)
|
||
- [Remote access](#remote-access)
|
||
- [Related endpoints](#related-endpoints-3)
|
||
- [RBAC / access control](#rbac-access-control)
|
||
- [Related endpoints](#related-endpoints-4)
|
||
- [APK builder](#apk-builder)
|
||
- [Related endpoints](#related-endpoints-5)
|
||
- [Git (Gitea)](#git-gitea)
|
||
- [OTA updates](#ota-updates)
|
||
- [Layout](#layout)
|
||
- [Publish (operator)](#publish-operator)
|
||
- [Heartbeat (device)](#heartbeat-device)
|
||
- [Auth & account security](#auth-account-security)
|
||
- [URL shortener](#url-shortener)
|
||
- [Non-HTTP services](#non-http-services)
|
||
- [Validation scripts](#validation-scripts)
|
||
<!-- /toc -->
|
||
|
||
**Documentation index:** [README.md](README.md)
|
||
|
||
---
|
||
|
||
## Validation snapshot
|
||
|
||
Run: `examples/crash_reporter/backend/scripts/validate_be_services.sh`
|
||
|
||
| Check | URL / target | 2026-06-10 |
|
||
|-------|----------------|------------|
|
||
| Hub | `/app/androidcast_project/` | ✅ HTTP 200 |
|
||
| Crashes console | `…/crashes/` | ✅ HTTP 302 → login |
|
||
| Graphs | `…/graphs/` | ✅ HTTP 302 → login |
|
||
| Builder | `…/build/` | ✅ HTTP 302 → login |
|
||
| Git | `…/git/` | ✅ HTTP 200 |
|
||
| OTA channel | `/v0/ota/channel/stable.json` | ⚠️ HTTP **404** (FE proxy OK; artifact or BE `location` missing) |
|
||
| Crash upload | `…/crashes/api/upload.php` GET | ✅ HTTP **400** (expects POST JSON) |
|
||
| Heartbeat | `…/crashes/api/heartbeat.php` GET | ✅ HTTP **405** (expects POST) |
|
||
| Graph upload | `…/graphs/api/graph_upload.php` GET | ✅ HTTP **400** (expects POST) |
|
||
| Two-factor | `…/crashes/two-factor` | ✅ HTTP 302 → login (no pending session) |
|
||
|
||
**Alerts — may need follow-up patch**
|
||
|
||
1. **OTA:** Public returns **404**; BE direct `curl -H 'Host: apps.f0xx.org' http://artc0…/v0/ota/channel/stable.json` returned HTML lander redirect (not JSON). Confirm BE `listen 80` has `location ^~ /v0/ota/` **before** catch-all, and publish `stable.json` under `ota-artifacts/v0/ota/channel/`.
|
||
2. **2FA:** Fixed in repo — `completeTotpLogin()` no longer clears pending session on wrong code; login redirects to `/two-factor` when pending. **Sync BE** (`Auth.php`, `index.php`, `two_factor_challenge.php`, `app.css`).
|
||
|
||
---
|
||
|
||
## Hub landing
|
||
|
||
**Web:** [https://apps.f0xx.org/app/androidcast_project/](https://apps.f0xx.org/app/androidcast_project/)
|
||
|
||
Marketing multipage shell; left nav links to Console, Git, Issues, Tickets, Graphs, Remote access, Builder. Shared session cookie `ac_crash_sess` path `/app/androidcast_project`.
|
||
|
||
| Kind | Path | Notes |
|
||
|------|------|--------|
|
||
| Static/PHP | `index.php` | Hub landing |
|
||
| CSS/JS | `hub.css`, `hub-landing.css`, `assets/hub-*.js` | Theme + i18n |
|
||
| Crashes assets | `…/crashes/assets/css/app.css` | Shared console theme |
|
||
|
||
No device ingest on hub root.
|
||
|
||
---
|
||
|
||
## Console home
|
||
|
||
**Web:** [https://apps.f0xx.org/app/androidcast_project/crashes/?view=home](https://apps.f0xx.org/app/androidcast_project/crashes/?view=home) (auth required)
|
||
|
||
Landing cards: Issues, New issue, Tickets, New ticket, Graphs; quick links to all services.
|
||
|
||
---
|
||
|
||
## Issues (crash reports)
|
||
|
||
**Web:** [https://apps.f0xx.org/app/androidcast_project/crashes/?view=reports](https://apps.f0xx.org/app/androidcast_project/crashes/?view=reports)
|
||
|
||
Triage anonymous crash JSON; grouped/by-time views; tag editor; tree expand.
|
||
|
||
### Related endpoints
|
||
|
||
| Method | Endpoint | Auth | Purpose |
|
||
|--------|----------|------|---------|
|
||
| `POST` | `…/crashes/api/upload.php` | None | Device crash ingest (JSON or gzip JSON) |
|
||
| `GET` | `…/crashes/api/reports.php` | Session | List/filter reports (console JS) |
|
||
| `POST` | `…/crashes/api/report_tags.php` | Session | Update report tags |
|
||
| `POST` | `…/crashes/api/report_viewed.php` | Session | Mark report viewed |
|
||
| `GET` | `…/crashes/api/tag_catalog.php` | Session | Tag suggestions |
|
||
| `GET` | `…/crashes/api/diag.php` | Optional | Diagnostics (may 403 at edge) |
|
||
|
||
### Upload via curl
|
||
|
||
```bash
|
||
BASE=https://apps.f0xx.org/app/androidcast_project/crashes
|
||
SAMPLE=examples/crash_reporter/sample_crash_java.json
|
||
RID="manual-$(date +%s)"
|
||
jq --arg rid "$RID" '.report_id = $rid' "$SAMPLE" | \
|
||
curl -sS -X POST "$BASE/api/upload.php" \
|
||
-H 'Content-Type: application/json' --data-binary @-
|
||
```
|
||
|
||
Gzip upload smoke: `examples/crash_reporter/backend/scripts/test_gzip_upload.sh`
|
||
|
||
### Bulk / synthetic issues
|
||
|
||
- Optional payload fields: `ingest.source`, `ingest.client_id`, `ingest.run_id` — see [OPEN_API.md](OPEN_API.md).
|
||
- Loop `upload.php` with varied `report_id` / fingerprints for load tests.
|
||
- Tag harness traffic `synthetic` via console or `report_tags.php`.
|
||
|
||
---
|
||
|
||
## Tickets
|
||
|
||
**Web:** [https://apps.f0xx.org/app/androidcast_project/crashes/?view=tickets](https://apps.f0xx.org/app/androidcast_project/crashes/?view=tickets)
|
||
|
||
Roadmap / QA tasks; workflow tags (`ticket` vs `issue` meta).
|
||
|
||
### Related endpoints
|
||
|
||
| Method | Endpoint | Auth | Purpose |
|
||
|--------|----------|------|---------|
|
||
| `GET` | `…/crashes/api/tickets.php` | Session | List tickets |
|
||
| `POST` | `…/crashes/api/ticket_create.php` | Session | Create ticket/issue |
|
||
| `POST` | `…/crashes/api/ticket_update.php` | Session | Update fields/tags |
|
||
| `POST` | `…/crashes/api/ticket_tags.php` | Session | Tag catalog ops |
|
||
| `POST` | `…/crashes/api/ticket_comments.php` | Session | Comments |
|
||
| `POST` | `…/crashes/api/ticket_upload.php` | Session | Attachment upload |
|
||
| `GET` | `…/crashes/api/ticket_attachments.php` | Session | List attachments |
|
||
| `GET` | `…/crashes/api/ticket_attachment.php` | Session | Download attachment |
|
||
|
||
### Create via UI
|
||
|
||
- Issues view: **New issue** → `ticket_create.js` → `ticket_create.php` with `issue` meta tag.
|
||
- Tickets view / home: **New ticket** → same API with `ticket` meta tag.
|
||
|
||
### Create via curl (session cookie)
|
||
|
||
```bash
|
||
BASE=https://apps.f0xx.org/app/androidcast_project/crashes
|
||
JAR=/tmp/ac-cookie.txt
|
||
curl -sS -c "$JAR" -b "$JAR" -X POST -d 'username=admin&password=admin' "$BASE/login" -o /dev/null
|
||
curl -sS -b "$JAR" -X POST -H 'Content-Type: application/json' \
|
||
-d '{"title":"API test","brief":"from curl","body":"","tags":[{"id":"ticket","label":"ticket","bg":"#6366f1"},{"id":"open","label":"open","bg":"#22c55e"}]}' \
|
||
"$BASE/api/ticket_create.php"
|
||
```
|
||
|
||
---
|
||
|
||
## Graphs
|
||
|
||
**Web:** [https://apps.f0xx.org/app/androidcast_project/graphs/](https://apps.f0xx.org/app/androidcast_project/graphs/)
|
||
|
||
Session metrics dashboards (user / slug / platform scopes); drag-reorder bricks; empty-session notice when no `graph_upload` data.
|
||
|
||
### Related endpoints
|
||
|
||
| Method | Endpoint | Auth | Purpose |
|
||
|--------|----------|------|---------|
|
||
| `GET` | `…/graphs/api/graphs.php?days=14` | Session | Aggregated metrics JSON |
|
||
| `POST` | `…/graphs/api/graph_upload.php` | Device/session | Ingest `graph_session` NDJSON/JSON |
|
||
|
||
Graphs PHP routes through crashes `public/index.php` (`?view=graphs`).
|
||
|
||
### Fake session data (dev / BE)
|
||
|
||
```bash
|
||
# Generate NDJSON
|
||
php examples/crash_reporter/backend/scripts/generate_fake_graph_sessions.php \
|
||
--devices=100 --per-device=6 --out=/tmp/sessions.ndjson
|
||
|
||
# SQL import on BE (when HTTP upload times out)
|
||
php examples/crash_reporter/backend/scripts/ndjson_to_graph_sql.php /tmp/sessions.ndjson | \
|
||
ssh alpine-be 'mariadb androidcast_crashes'
|
||
```
|
||
|
||
API smoke: `examples/crash_reporter/backend/scripts/test_graphs_api.sh`
|
||
|
||
---
|
||
|
||
## Remote access
|
||
|
||
**Web:** [https://apps.f0xx.org/app/androidcast_project/crashes/?view=remote_access](https://apps.f0xx.org/app/androidcast_project/crashes/?view=remote_access) (RBAC: `remote_access_view`)
|
||
|
||
Operator console: whitelist devices, open WireGuard or RSSH sessions.
|
||
|
||
### Related endpoints
|
||
|
||
| Method | Endpoint | Auth | Purpose |
|
||
|--------|----------|------|---------|
|
||
| `POST` | `…/crashes/api/remote_access.php?action=…` | Session | `whitelist`, `open_session`, `dashboard`, … |
|
||
| `POST` | `…/crashes/api/heartbeat.php` | None | Device RA poll (`heartbeat.type=ra`) |
|
||
|
||
Device flow: app polls `heartbeat.php` → operator uses console → next poll returns `connect` payload.
|
||
|
||
Smoke: `CRASHES_BASE=https://apps.f0xx.org/app/androidcast_project/crashes examples/crash_reporter/backend/scripts/test_remote_access_api.sh`
|
||
|
||
Prod checklist: `examples/crash_reporter/backend/scripts/verify_remote_access_prod.sh`
|
||
|
||
---
|
||
|
||
## RBAC / access control
|
||
|
||
**Web:** [https://apps.f0xx.org/app/androidcast_project/crashes/?view=rbac](https://apps.f0xx.org/app/androidcast_project/crashes/?view=rbac) (admin)
|
||
|
||
### Related endpoints
|
||
|
||
| Method | Endpoint | Auth | Purpose |
|
||
|--------|----------|------|---------|
|
||
| `GET/POST` | `…/crashes/api/rbac.php` | Session | Roles, memberships, lockouts |
|
||
| `GET/POST` | `…/crashes/api/users.php` | Session | User admin |
|
||
|
||
Smoke: `examples/crash_reporter/backend/scripts/test_rbac_api.sh`
|
||
|
||
---
|
||
|
||
## APK builder
|
||
|
||
**Web:** [https://apps.f0xx.org/app/androidcast_project/build/](https://apps.f0xx.org/app/androidcast_project/build/)
|
||
|
||
Docker CI: trigger builds, stream logs, OTA artifact option.
|
||
|
||
### Related endpoints
|
||
|
||
| Method | Endpoint | Auth | Purpose |
|
||
|--------|----------|------|---------|
|
||
| `GET` | `…/build/api/health.php` | Session | Docker/socket health |
|
||
| `GET` | `…/build/api/builds.php` | Session | Build history |
|
||
| `POST` | `…/build/api/build_trigger.php` | Session | Start build |
|
||
| `GET` | `…/build/api/build_log.php?id=` | Session | Log tail |
|
||
| `POST` | `…/build/api/build_actions.php` | Session | Cancel / rerun |
|
||
|
||
Shared login with crashes (`ac_crash_sess`). See [BUILD_DEPLOY.md](BUILD_DEPLOY.md).
|
||
|
||
---
|
||
|
||
## Git (Gitea)
|
||
|
||
**Web:** [https://apps.f0xx.org/app/androidcast_project/git/](https://apps.f0xx.org/app/androidcast_project/git/)
|
||
|
||
FE proxies to `http://artc0.intra.raptor.org:3000/` (Gitea). Canonical git remote: `git://f0xx.org/android_cast`.
|
||
|
||
No Android Cast PHP on this path.
|
||
|
||
---
|
||
|
||
## OTA updates
|
||
|
||
**Web channel:** [https://apps.f0xx.org/v0/ota/channel/stable.json](https://apps.f0xx.org/v0/ota/channel/stable.json)
|
||
|
||
Device setting: `ota.base_url` in `examples/settings.json` → `https://apps.f0xx.org/v0/ota/channel/stable.json`.
|
||
|
||
### Layout
|
||
|
||
See [OTA.md](OTA.md): `v0/ota/channel/{stable,staging,…}.json` → manifest → `.otapkg` / `.otabundle.zip`.
|
||
|
||
### Publish (operator)
|
||
|
||
```bash
|
||
./scripts/generate-ota-v0.sh app/build/outputs/apk/release/app-release.apk \
|
||
https://apps.f0xx.org ./out/ota staging
|
||
OTA_DEPLOY_TARGET=… ./scripts/deploy-ota-staging.sh
|
||
```
|
||
|
||
BE mount: `…/androidcast_project/ota-artifacts/v0/` — `prepare-be-ota-mount.sh`.
|
||
|
||
**Status 2026-06-10:** FE `/v0/ota/` → BE :80 configured; channel file **not yet served** (404 / lander) — publish artifacts + verify BE nginx `location ^~ /v0/ota/`.
|
||
|
||
---
|
||
|
||
## Heartbeat (device)
|
||
|
||
**Web:** none (app service only)
|
||
|
||
**Endpoint:** `POST https://apps.f0xx.org/app/androidcast_project/crashes/api/heartbeat.php`
|
||
|
||
JSON body with `heartbeat` object: session stats, remote-access poll (`type: ra`), etc.
|
||
|
||
```bash
|
||
curl -sS -X POST -H 'Content-Type: application/json' \
|
||
-d '{"heartbeat":{"type":"ping","device_id":"test-device"}}' \
|
||
https://apps.f0xx.org/app/androidcast_project/crashes/api/heartbeat.php
|
||
```
|
||
|
||
---
|
||
|
||
## Auth & account security
|
||
|
||
| Page | URL |
|
||
|------|-----|
|
||
| Login | […/crashes/login](https://apps.f0xx.org/app/androidcast_project/crashes/login) |
|
||
| Register | […/crashes/register](https://apps.f0xx.org/app/androidcast_project/crashes/register) |
|
||
| Two-factor challenge | […/crashes/two-factor](https://apps.f0xx.org/app/androidcast_project/crashes/two-factor) |
|
||
| Account security (TOTP enroll) | […/crashes/account-security](https://apps.f0xx.org/app/androidcast_project/crashes/account-security) |
|
||
| Email verify | `…/crashes/verify-email?token=` |
|
||
|
||
| Method | Endpoint | Purpose |
|
||
|--------|----------|---------|
|
||
| `POST` | `…/crashes/api/auth_register.php` | Registration API |
|
||
|
||
TOTP verify script: `examples/crash_reporter/backend/scripts/verify_auth_totp.php`
|
||
|
||
Flow doc: [20260607-2FA-email-mobile-auth-flow.md](20260607-2FA-email-mobile-auth-flow.md)
|
||
|
||
---
|
||
|
||
## URL shortener
|
||
|
||
**Web:** [https://s.f0xx.org](https://s.f0xx.org) · **Admin:** […/crashes/?view=short_links](https://apps.f0xx.org/app/androidcast_project/crashes/?view=short_links) (RBAC: company owner/admin)
|
||
|
||
| Kind | Path | Notes |
|
||
|------|------|--------|
|
||
| Health | `GET /api/v1/health` | JSON `status: ok`, DB ping |
|
||
| Shorten | `GET/POST /api/v1/shorten` | Bearer token required |
|
||
| QR | `GET /api/v1/qr/{slug}.png` | PNG (`libqrencode-tools` on BE) |
|
||
| Redirect | `GET /{slug}` | `302` → origin; `410` if expired |
|
||
| Admin UI | `…/crashes/?view=short_links` | Mint/revoke bearers, list links (session RBAC) |
|
||
|
||
Spec: [specs/20100611_3_url_shortener.md](specs/20100611_3_url_shortener.md) · code: [backend/url-shortener/](../backend/url-shortener/)
|
||
|
||
```bash
|
||
curl -fsS https://s.f0xx.org/api/v1/health
|
||
# Bearer: hub Short links → Mint, or php backend/url-shortener/scripts/seed_bearer.php (CLI)
|
||
export BEARER=… BASE=https://s.f0xx.org backend/url-shortener/scripts/demo_curl.sh
|
||
CRASHES_BASE=https://apps.f0xx.org/app/androidcast_project/crashes ./examples/crash_reporter/backend/scripts/test_short_links_api.sh
|
||
```
|
||
|
||
---
|
||
|
||
## Non-HTTP services
|
||
|
||
| Service | Access | Used by |
|
||
|---------|--------|---------|
|
||
| **WireGuard** | `ra.apps.f0xx.org:45340/udp` | Remote access lab mode |
|
||
| **RSSH bastion** | `ra.apps.f0xx.org:443/tcp` | Reverse SSH alpha mode |
|
||
| **Cast LAN** | UDP/TCP `:41235`, discovery `:41234` | Screen mirror (no BE) |
|
||
| **MQTT OTA** (optional) | `mqtt://host:port/v0/ota/…` | Retained topic payloads |
|
||
|
||
---
|
||
|
||
## Validation scripts
|
||
|
||
| Script | Purpose |
|
||
|--------|---------|
|
||
| `validate_be_services.sh` | Public HTTP smoke (this doc’s snapshot) |
|
||
| `test_gzip_upload.sh` | Crash upload plain + gzip |
|
||
| `test_graphs_api.sh` | Graphs JSON API |
|
||
| `test_remote_access_api.sh` | RA whitelist + session |
|
||
| `test_rbac_api.sh` | RBAC API |
|
||
| `test_rssh_api.sh` | RSSH mode API |
|
||
| `test_tags_api.sh` | Report tag API |
|
||
| `verify_remote_access_prod.sh` | Prod RA checklist |
|
||
|
||
FE nginx template: `examples/crash_reporter/backend/nginx/fe/apps.conf`
|
||
Regenerate FE patch: `scripts/generate-fe-nginx-patch.sh`
|