Skip to content

DOCS-3472: Add documentation for Viam Apps #4335

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
4 changes: 4 additions & 0 deletions assets/scss/_styles_project.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3365,6 +3365,10 @@ div.tablestep > table td, div.tablestep table td {
margin-left: 1.0rem;
}

.td-content > ol li {
margin-bottom: 0.5rem;
}

.td-content > ul {
padding-left: 1rem;
}
Expand Down
Binary file modified assets/tutorials/air-quality-fleet/get-readings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions docs/dev/reference/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ date: "2024-09-18"
# updated: "" # When the content was last entirely checked
---

{{% changelog color="changed" title="Deprecated explicit dependencies" date="2025-05-30" %}}

You can now create and use Viam apps to build custom applications that interact with your Viam-powered machines through the Viam SDKs.
For more information, see [Viam apps](/operate/control/viam-app/).

{{% /changelog %}}

{{% changelog color="changed" title="Deprecated explicit dependencies" date="2025-05-05" %}}

Some older modules use "explicit dependencies" which require users to list the names of dependencies in the `depends_on` field of the resource's configuration, for example:
Expand Down
297 changes: 297 additions & 0 deletions docs/operate/control/viam-app.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
---
title: "Create a Viam app"
linkTitle: "Create a Viam app"
weight: 5
layout: "docs"
type: "docs"
description: "Create and deploy a custom web interface for your machines without managing hosting and authentication."
---

Create and deploy a custom web interface for your machines without managing hosting and authentication.
Once deployed, your app is accessible from a dedicated URL (`appname_publicnamespace.viamapplications.com`), and hosting and authentication is handled for you.

Users log into your app and select a machine they have access to.
The app then renders your custom interface for interacting with the user's machine.

{{<gif webm_src="/spa.webm" mp4_src="/spa.mp4" alt="Sample web app" max-width="500px">}}

## Requirements

{{< expand "Install the Viam CLI and authenticate." >}}
Install the Viam CLI using the option below that matches your system architecture:

{{< readfile "/static/include/how-to/install-cli.md" >}}

Then authenticate your CLI session with Viam using one of the following options:

{{< readfile "/static/include/how-to/auth-cli.md" >}}

{{< /expand >}}

## Build a custom web interface

You can build a custom web interface to access your machines using your preferred framework like React, Vue, Angular, or others.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment below about this heading. Along those lines, I think this section should link to examples of these, and basically say "go off and develop an app then come back"


### Access machines from your app
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But, shouldn't users just be able to log in? It's unclear who needs to do this and when and why


When logging into a Viam app and selecting a machine to use it with, the machine's API key is stored as a cookie.
You can access the data from your browser's cookies as follows:

```ts {class="line-numbers linkable-line-numbers" data-line=""}
import Cookies from "js-cookie";

let apiKeyId = "";
let apiKeySecret = "";
let hostname = "";
let machineId = "";

machineCookie = window.location.pathname.split("/")[2];
({
id: apiKeyId,
key: apiKeySecret,
hostname: hostname,
machineId: machineId
} = JSON.parse(Cookies.get(machineCookie)!));

For developing your app on localhost, add the same information to your browser's cookies:

1. Navigate to [Camera Viewer](https://camera-viewer_naomi.viamapplications.com/).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add explanation of why we are going to your demo app? If I'm making my own app that has nothing to do with cameras this feels like a strange step

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that'll go away once the cookie issue is fixed so will leave for not

2. Log in and select the machine you'd like to use for testing.
3. Open Developer Tools and go to the console.
4. Execute the following JavaScript to obtain the cookies you need:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[opt] Maybe tell users how (i.e., "Type "allow pasting" into the console, then paste the following script....")
Though maybe this is browser-dependent and they'll figure it out (like I did)...feel free to disregard, just a thought


```js {class="line-numbers linkable-line-numbers" data-line=""}
function generateCookieSetterScript() {
// Get all cookies from current page
const currentCookies = document.cookie.split(";");
let cookieSetterCode = "// Cookie setter script for localhost\n";
cookieSetterCode +=
"// Copy and paste this entire script into your browser console when on localhost\n\n";

// Process each cookie
let cookieCount = 0;
currentCookies.forEach((cookie) => {
if (cookie.trim()) {
// Extract name and value from the cookie
const [name, value] = cookie.trim().split("=");

// Add code to set this cookie
cookieSetterCode += `document.cookie = "${name}=${value}; path=/";\n`;
cookieCount++;
}
});

// Add summary comment
cookieSetterCode += `\nconsole.log("Set ${cookieCount} cookies on localhost");\n`;

// Display the generated code
console.log(cookieSetterCode);

// Create a textarea element to make copying easier
const textarea = document.createElement("textarea");
textarea.value = cookieSetterCode;
textarea.style.position = "fixed";
textarea.style.top = "0";
textarea.style.left = "0";
textarea.style.width = "100%";
textarea.style.height = "250px";
textarea.style.zIndex = "9999";
document.body.appendChild(textarea);
textarea.focus();
textarea.select();
}

// Execute the function
generateCookieSetterScript();
```

5. Copy the resulting script. It will look like this:

```js {class="line-numbers linkable-line-numbers" data-line=""}
// Cookie setter script for localhost
// Copy and paste this entire script into your browser console when on localhost

document.cookie = "<SECRET COOKIE INFO>; path=/";
document.cookie = "machinesWhoseCredentialsAreStored=<MACHINE ID>; path=/";

console.log("Set 2 cookies on localhost");
```

6. Open the app you are building on localhost and run the resulting script.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I haven't started building an app yet have I? If I should have, the top of the doc should be tweaked to indicate this.
"Build a custom web interface" to me implies that I'm about to get instructions for how to build a custom web interface instead of just how to connect one to my machines. Maybe "Build a custom web interface" or similar should be a prereq

7. Reload your app.

### Configure routing

When using your deployed application, static files will be accessible at `https://your-app-name_your-public-namespace.viamapplications.com/machine/<machine-id>/`.
If your HTML file loads other files, use relative paths to ensure your files are accessible.

## Deploy your web interface as a Viam app

To deploy your app with Viam you must package it as a module and upload it using the Viam CLI.

{{< table >}}
{{% tablestep number=1 %}}

**Create a <FILE>meta.json</FILE>** file for your module using this template:

{{< tabs >}}
{{% tab name="Template" %}}

```json
{
"module_id": "your-namespace:your-module",
"visibility": "public",
"url": "https://github.com/your-org/your-repo",
"description": "Your module description",
"applications": [
{
"name": "your-app-name",
"type": "single_machine",
"entrypoint": "dist/index.html"
}
]
}
```

{{% /tab %}}
{{% tab name="Example" %}}

```json
{
"module_id": "acme:dashboard",
"visibility": "public",
"url": "https://github.com/acme/dashboard",
"description": "An example dashboard for a fictitious company called Acme.",
"applications": [
{
"name": "dashboard",
"type": "single_machine",
"entrypoint": "dist/index.html"
}
]
}
```

{{% /tab %}}
{{< /tabs >}}

This file specifies the contents of the module.
It is required for your module.

{{% expand "Click to view more information on attributes." %}}

<!-- prettier-ignore -->
| Name | Type | Inclusion | Description |
|------|------|-----------|-------------|
| `module_id` | string | **Required** | The module ID, which includes the organization name and the module name. `module_id` uniquely identifies your module. |
| `visibility` | string | **Required** | Must be `"public"`. |
| `description` | string | **Required** | A description of your module and what it provides. |
| `url` | string | Optional | The URL of the GitHub repository containing the source code of the module. |
| `applications` | array | Optional | Objects that provide information about the [apps](/operate/control/viam-app/) associated with the module. |
| `models` | array | Optional | Empty unless you are shipping the app alongside models. For information on how to add models, see [Integrate other hardware](/operate/get-started/other-hardware/). |

{{% /expand%}}

The `applications` field is an array of application objects with the following properties:

<!-- prettier-ignore -->
| Property | Type | Description |
| ------------ | ------ | ----------- |
| `name` | string | The name of your application, which will be a part of the app's URL (`name_publicnamespace.viamapplications.com`). For more information on valid names see [Valid application identifiers](/operate/reference/naming-modules/#valid-application-identifiers). |
| `type` | string | The type of application (currently only `"single_machine"` is supported). |
| `entrypoint` | string | The path to the HTML entry point for your application. The `entrypoint` field specifies the path to your application's entry point. For example: <ul><li><code>"dist/index.html"</code>: Static content rooted at the `dist` directory</li><li><code>"dist/foo.html"</code>: Static content rooted at the `dist` directory, with `foo.html` as the entry point</li><li><code>"dist/"</code>: Static content rooted at the `dist` directory (assumes `dist/index.html` exists)</li><li><code>"dist/bar/foo.html"</code>: Static content rooted at `dist/bar` with `foo.html` as the entry point</li></ul> |

{{% /tablestep %}}
{{% tablestep number=2 %}}
**Register your module** with Viam:

{{< tabs >}}
{{% tab name="Template" %}}

```sh {class="command-line" data-prompt="$" data-output="3-10"}
viam module create --name="app-name" --public-namespace="namespace"
```

{{% /tab %}}
{{% tab name="Example" %}}

```sh {class="command-line" data-prompt="$" data-output="3-10"}
viam module create --name="air-quality" --public-namespace="naomi"
```

{{% /tab %}}
{{< /tabs >}}

{{% /tablestep %}}
{{% tablestep number=3 %}}

**Package your static files and your <FILE>meta.json</FILE> file and upload them** to the Viam Registry:

```sh {class="command-line" data-prompt="$" data-output="3-10"}
tar -czvf module.tar.gz <static-website-files> meta.json
viam module upload --upload=module.tar.gz --platform=any --version=0.0.1
```

For subsequent updates run these commands again with an updated version number.

{{% /tablestep %}}
{{< /table >}}

## Access your app

After uploading your module with the application configuration, your application will be available at:

```txt
https://your-app-name_your-public-namespace.viamapplications.com
```

Users will be prompted to authenticate with their Viam credentials before accessing your application:

1. User navigates to `your-app-name_your-public-namespace.viamapplications.com`.
1. User authenticates with Viam credentials.
1. User selects an organization, location, and machine.
1. User is redirected to `your-app-name_your-public-namespace.viamapplications.com/machine/{machine-id}`.
1. Your application is rendered with access to the selected machine.

## Example

<!-- For a TypeScript example see [Monitor Air Quality with a Fleet of Sensors](/tutorials/control/air-quality-fleet/). -->

For a React app that shows camera feeds for a machine, see [Viam Camera Viewer](https://github.com/viam-labs/viam-camera-viewer).

## Limitations

- Apps currently only support single-machine applications.
- All modules with apps must have public visibility.
- The page will always render the latest version.
- Browsers with cookies disabled are not supported.
- Viam apps serve static files.
If you are building an app with server-side rendering or need other back-end capabilites, Viam apps is not the right choice.

## Security considerations

- Customer apps are stored publicly on the internet, so avoid uploading sensitive information in your application code or assets.
- API keys and secrets are stored in the browser's cookies.
- Users authenticate with FusionAuth.

## FAQ

### Can I use a custom domain?

If you want to serve your side from a domain other than `your-app-name_your-public-namespace.viamapplications.com`, you can set this up with your DNS provider.

To configure an apex domain (`example.com`) and the `www` subdomain, set the following values:

<!-- prettier-ignore -->
| Domain | DNS record type | DNS record name | DNS record value |
| ------ | --------------- | --------------- | ---------------- |
| `example.com` | `A` | `@` | `34.8.79.17` |
| `example.com` | `ANAME` or `ALIAS` | `@` | `your-app-name_your-public-namespace.viamapplications.com` |
| `www.examples.com` | `CNAME` | `SUBDOMAIN.example.com` | `your-app-name_your-public-namespace.viamapplications.com` |

To configure a subdomain, set the following values:

<!-- prettier-ignore -->
| Domain | DNS record type | DNS record name | DNS record value |
| ------ | --------------- | --------------- | ---------------- |
| Subdomain (`www.examples.com` or `app.example.com`) | `CNAME` | `SUBDOMAIN.example.com` | `your-app-name_your-public-namespace.viamapplications.com` |
5 changes: 5 additions & 0 deletions docs/operate/control/web-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ The TypeScript SDK includes:
- Implementation of the standard component and service APIs to control your hardware and software
- Authentication tools so users can log in securely

{{< alert title="Tip: Host your app on Viam" color="tip" >}}
You can host most apps by [deploying them as Viam apps](/operate/control/viam-app/).
If your app requires server-side rendering or other back-end functionality, self-host your app instead.
{{< /alert >}}

## Install the TypeScript SDK

Run the following command in your terminal to install the Viam TypeScript SDK:
Expand Down
16 changes: 11 additions & 5 deletions docs/operate/get-started/other-hardware/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -987,16 +987,16 @@ Do not change the <code>module_id</code>.</p>
</tr>
<tr>
<td><code>models</code></td>
<td>object</td>
<td><strong>Required</strong></td>
<td><p>A list of one or more {{< glossary_tooltip term_id="model" text="models" >}} provided by your custom module. You must provide at least one model, which consists of an <code>api</code> and <code>model</code> key pair. If you are publishing a public module (<code>"visibility": "public"</code>), the namespace of your model must match the <a href="/operate/reference/naming-modules/#create-a-namespace-for-your-organization">namespace of your organization</a>.</p>
<td>array</td>
<td>Optional</td>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<td>Optional</td>
<td><strong>Required</strong> if your module provides models</td>

[opt] Maybe better to emphasize like this since it's not just plain optional, and this is within the integrate hardware section?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes that column too long. If anyone makes a mistake with this the CLI will tell them about it and mostly this is auto-generated, so I'm not worried that there will be confusion.

<td><p>A list of one or more {{< glossary_tooltip term_id="model" text="models" >}} provided by your custom module. You must provide at least one model in the models array or one application in the applications array. A model consists of an <code>api</code> and <code>model</code> key pair. If you are publishing a public module (<code>"visibility": "public"</code>), the namespace of your model must match the <a href="/operate/reference/naming-modules/#create-a-namespace-for-your-organization">namespace of your organization</a>.</p>
<p>You are strongly encouraged to include a <code>markdown_link</code> to the section of the README containing configuration information about each model, so that the section will be displayed alongside the configuration panel when configuring the model. For example, <code>"README.md#configure-your-meteo_pm-sensor"</code>. Please also include a <code>short_description</code> describing what hardware the model supports.</p></td>
</tr>
<tr>
<td><code>entrypoint</code></td>
<td>string</td>
<td><strong>Required</strong></td>
<td>The name of the file that starts your module program. This can be a compiled executable, a script, or an invocation of another program. If you are providing your module as a single file to the <code>upload</code> command, provide the path to that single file. If you are providing a directory containing your module to the <code>upload</code> command, provide the path to the entry point file contained within that directory.</td>
<td>Optional</td>
<td>The name of the file that starts your module program. This can be a compiled executable, a script, or an invocation of another program. If you are providing your module as a single file to the <code>upload</code> command, provide the path to that single file. If you are providing a directory containing your module to the <code>upload</code> command, provide the path to the entry point file contained within that directory. Required if you are shipping a model.</td>
</tr>
<tr>
<td><code>build</code></td>
Expand All @@ -1016,6 +1016,12 @@ Do not change the <code>module_id</code>.</p>
<td>Optional</td>
<td>Enables VS Code hover and autocomplete as you edit your module code. Gets auto-generated when you run <code>viam module generate</code> or <code>viam module create</code>. Has no impact on the module's function.</td>
</tr>
<tr>
<td><code>applications</code></td>
<td>array</td>
<td>Optional</td>
<td>Objects that provide information about the [apps](/operate/control/viam-app/) provided by the module.</td>
</tr>

</table>

Expand Down
Loading
Loading