Local App Onboarding
Source: qar-streaming-c/examples/local_app_onboarding.c. Conceptual background: Onboarding and Sessions.
Local App Onboarding
What you build
The smallest source app that joins QAROS on the Hub machine. It loads the installed runtime, asks the Hub to let it in, and exits with a code that says what happened. The first time, QAROS asks the user to allow the app; later starts of the same executable join silently.
This is how a source app on the Hub machine joins QAROS. There is nothing to type and no onboarding id to store. An app on another machine pairs with a code instead (Onboarding and Rejoin).
What you will learn:
- how QarOnboardLocalAppExt joins, and what the user sees during the approval
- why a local app always loads the installed runtime
- how to follow the approval wait through progress messages
- which status codes a refusal comes with, and what to tell the user for each
Before you start
- Work through Dynamic Loading first: this program loads the runtime the same way.
- Windows, with QAROS installed on the machine the app runs on. When the Hub is not running, joining as a local app starts it, and the Hub keeps running after the app exits. Elsewhere the call fails with QAR_STATUS_NOT_IMPLEMENTED.
- Someone at the Hub machine to click Allow on the first run. The prompt waits up to 120 seconds by default.
Run it
The only argument is the approval timeout in milliseconds. Absent or 0 means the Hub's default of 120 s; the Hub caps longer waits at 10 minutes. The program takes no library path: like a shipped app, it always runs the default lookup.
On the first run, the expected output while the approval prompt is open and after the user clicks Allow is this. The lines before Joined QAROS. are the runtime's own progress messages:
Later starts of the same executable print the same lines without Waiting for approval in QAROS hub.
Exit codes, for scripts:
| Code | Status | Meaning |
|---|---|---|
| 0 | joined | |
| 1 | any other | other error, or an approval timeout that is not a number |
| 2 | no runtime loaded: QAROS is not installed, or its runtime could not be loaded | |
| 3 | QAR_STATUS_ONBOARDING_HUB_UNREACHABLE (1802) | no Hub answers on this machine, and none could be started |
| 4 | QAR_STATUS_ONBOARDING_APPROVAL_DENIED (1805) | Deny, a closed prompt, or a revoked approval |
| 5 | QAR_STATUS_ONBOARDING_APPROVAL_TIMEOUT (1806) | nobody answered in time |
| 6 | QAR_STATUS_ONBOARDING_HUB_NOT_AUTHENTIC (1807) | the program answering on this machine is not the installed QAROS |
| 7 | QAR_STATUS_ONBOARDING_APP_IDENTITY_MISMATCH (1808) | the executable no longer matches its approval |
Walkthrough
1. Read the approval timeout
The API reads a zero timeout as "the Hub's default", so a mistyped argument that turned into 0 would silently become 120 s. The program refuses text that is not a whole number of milliseconds and exits with 1:
2. Load the installed runtime
A local app always calls qar_library_load with NULL, never with a path of its own (Load the installed runtime). The Hub refuses a runtime of another version or flavor than its own, such as a private copy shipped with the app, with QAR_STATUS_ONBOARDING_FAILED and the reason in the message.
Console logging is off: this app talks to a user, not a developer. When no runtime loads, the header's loader has already printed where it looked; the program adds one line for the user and exits with 2.
3. Create a runtime
qar_runtime_init_default() is all a local app needs. With QarRuntimeInit::runtime_binaries_folder_path empty, the runtime uses the folder the library was loaded from, the installed bin folder.
4. Join, and follow the approval
QarOnboardLocalAppExt, chained as the only mode extension, asks the Hub on this machine to let the process in. The Hub identifies the process from Windows: its path, file hash, signature and user. Your display_name is shown to the user separately, labelled "App says:".
- Approved before and unchanged: the session comes back at once, with no prompt.
- Otherwise QAROS shows "Allow local_app_onboarding.exe to join QAROS?" in the Visualizer and as a tray window, in front of every other window. Deny is the default button, and closing the prompt counts as Deny.
- Allow saves a trust record on the Hub. For an unsigned executable it holds the exact path and file hash, so every rebuild asks again. That is expected during development; sign release builds and users approve them once (The trust record).
qar_runtime_onboard blocks until the Hub answers, which can take minutes. The progress callback shows what it waits for. The library calls it on its own thread while your thread is blocked, and the last call returns before qar_runtime_onboard does. The callback only prints, so it needs no state:
The program passes no cancel token, so it can only wait. An application passes a QarCancelToken and cancels it when the user quits.
A local app is stateless. The onboarding id comes back zero, and there is nothing to save: every start calls qar_runtime_onboard again, and the Hub remembers the approval. qar_runtime_rejoin and qar_runtime_forget do not apply.
5. Tell the user, and leave
Each refusal comes with its own status code, and each needs a different answer from the user. example_local_app_exit_code, shown in step 4, maps them to the exit codes above and prints what to do. None of them is worth retrying silently. A timeout may be offered again; a denial is retried only when the user asks; a Hub that is not authentic is never retried (Status codes and what to tell the user).
The sample programs that use a session join the same way when they are given neither a stored id nor a code (example_join_session in common.h).
Ownership and teardown in C
- The session is yours. On success qar_runtime_onboard writes the only handle to it. qar_session_handle_destroy ends it; the Hub keeps the approval, not the session.
- Reverse order. The session, then the runtime (qar_runtime_handle_destroy), then the library.
- The init structures are copied. init and local_app live on the stack and are only read during the call.
- The progress callback runs only during the blocking call. Anything it reaches through its state pointer must live that long.
- Nothing is written to disk by the app.
Troubleshooting
| What you see | Cause | What to do |
|---|---|---|
| exit 2, Could not load QAROS: install or repair it; the line above says why. after qar-streaming-c.dll is not beside this module, ... | no runtime in any step of the lookup | install QAROS (Installation) |
| exit 2, the same line after QAROS_RUNTIME_DIR is '...', but no loadable qar-streaming-c.dll is there. | the variable names a folder without a loadable runtime; the lookup stops there | fix or unset QAROS_RUNTIME_DIR |
| exit 2, the same line after The installed QAROS runtime in '...' could not be loaded. | a broken installation | repair or reinstall QAROS |
| exit 2, the same line after Failed to load symbol '...' | the runtime is older than this repository's header | install a QAROS at least as new as the header |
| exit 3, Could not start or reach QAROS on this machine. Start QAROS from the Start menu, then start this app again. | QAROS is installed but its Hub does not answer, and the runtime did not start it | start QAROS from the Start menu, then the app again |
| exit 4 | someone clicked Deny, closed the prompt, or an operator revoked the app | start the app again and click Allow |
| exit 5 | nobody answered within the timeout | answer the prompt in the Visualizer or the tray window; pass a longer timeout if users need it |
| exit 6 | the program serving the local enrollment is not the installed, signed QAROS | do not retry; reinstall QAROS from the official installer |
| exit 7 | the executable was moved, copied, re-signed or changed since its approval | revoke the old approval in the Visualizer (Approved local apps) and start again |
| exit 1, QAR_STATUS_ONBOARDING_FAILED with a runtime version or flavor in the message | the app loaded a runtime other than the installed one, through QAROS_RUNTIME_DIR or a copy beside the executable | unset the variable, remove the copy |
| exit 1, approval timeout '...' is not a number of milliseconds | the argument is not a number | pass milliseconds, or nothing |
| the prompt comes back after every build | the executable is unsigned, so its approval is for one exact file | expected during development |
More in Troubleshooting: Joining QAROS as a local app.
Next
- CPU Rendering: join the same way, then stream frames to the target apps.
- Onboarding and Sessions: every onboarding mode, and what changes when QAROS itself starts your app.
- Local App Onboarding in Rust: the same program with the Rust binding.
Generated via doxygen2docusaurus 2.2.1 by Doxygen 1.9.8.