Extend Companion without creating a second ExamNova.
The Laravel backend remains authoritative. Companion Core supplies reusable infrastructure; the ExamNova app supplies product screens and calls narrowly scoped API v1 routes.
Responsibility and repository layout
mobile/
packages/companion-core/ discovery, API client, secure sessions, encrypted sync, compatibility, telemetry
apps/examnova/ Expo app, role navigation, product screens, native configuration
app/Http/Controllers/Api/Mobile/V1/
app/Services/Mobile/
documentation/mobile-api.openapi.yaml
Authentication and tenant invariants
- Access and refresh secrets are returned once; only SHA-256 hashes are stored server-side.
- Access is short lived, refresh rotates, logout revokes, and device revocation ends every active token for that device.
- Every protected request rechecks user status, device status, institute membership, tenant status, current permission-derived scope, resource ownership, and record state.
- Cross-tenant resources return 404; same-tenant resources owned by another student return 403.
- App/API compatibility is negotiated before authentication using explicit version headers.
API contract
The source contract is documentation/mobile-api.openapi.yaml. The base path is /api/mobile/v1. Clients send X-Ovion-App-Version and X-Ovion-API-Version; authenticated requests also send a bearer access token.
Offline synchronization
- The client encrypts each JSON mutation with AES-GCM and a random nonce; the 256-bit key stays in OS secure storage.
- Every answer packet carries a mutation UUID, idempotency key, per-answer client UUID, and monotonic sync version.
- The backend records the request hash and prior response. Identical replay returns the prior outcome; a reused key with a different hash returns 409.
- ExamAttemptService rejects stale versions so delayed packets cannot overwrite newer accepted answers.
- Logout, revocation, and retention cleanup delete queued material and the local key.
Push delivery and receipts
Companion registers a native FCM or APNs token. The model encrypts the raw token and stores a separate hash for lookup. Product notifications create delivery rows and queued jobs. Provider payloads use a generic title/body plus message UUID, event, and role-safe deep link; they contain no student, exam, result, or answer details. The app reports received/opened/dismissed receipts.
Domains and verified links
- Canonical product:
https://examnova.ovion-tech.xyz. - Discovery:
/.well-known/ovion-companion.jsonand/api/mobile/v1/discovery. - Android association:
/.well-known/assetlinks.jsonwith the production certificate SHA-256 fingerprint. - Apple association:
/.well-known/apple-app-site-associationwith the Apple team and bundle identifiers. - Self-hosted customers use the same paths on their verified domain.
Configuration
COMPANION_CANONICAL_URL=https://examnova.ovion-tech.xyz
COMPANION_MIN_APP_VERSION=1.0.0
COMPANION_LATEST_APP_VERSION=1.0.0
COMPANION_ANDROID_SHA256_FINGERPRINTS=AA:BB:...
COMPANION_APPLE_TEAM_ID=TEAMID
COMPANION_FCM_PROJECT_ID=project-id
COMPANION_FCM_ACCESS_TOKEN=short-lived-oauth-token
COMPANION_APNS_TOPIC=xyz.oviontech.examnova
COMPANION_APNS_AUTH_TOKEN=short-lived-provider-tokenProvider tokens belong in protected environment configuration and must be rotated. Do not commit signing material, APNs keys, service-account JSON, passwords, or production tokens.
Build, signing, and release
- Run
npm install,npm run typecheck,npm test, andnpm run doctorfrommobile/. - Generate native projects with
npx expo prebuild. Android local preview uses Gradle; production AAB uses organization-owned upload signing. - Configure iOS signing, entitlements, APNs, and Universal Links in the owner team's Apple account; create the archive on macOS or approved EAS infrastructure.
- Verify Android and iOS on physical devices, online/offline primary workflow, authentication, tenant isolation, push receipt, deep links, version enforcement, accessibility, and privacy.
- Publish checksums, store metadata, privacy declarations, release notes, supported API versions, rollback/revocation steps, and a retained symbol/mapping archive.
Required tests
- Backend feature tests: discovery, versions, login/refresh/logout, hashed secrets, revocation, scope recalculation, ownership, cross-tenant concealment, exam lifecycle, idempotent sync, stale conflict, push encryption, receipts.
- Core tests: semantic compatibility, discovery validation, encryption round-trip, queue ordering, conflict handling, and telemetry redaction.
- App tests: role routing, preview labeling, assignment state, answer controls, offline status, final submission confirmation, dynamic brand contrast, and deep-link fallback.
- Native smoke: clean Android APK/AAB and iOS archive, manifest/entitlements inspection, physical-device installation, permission prompts, background/foreground recovery.