From a6bb349f0b010928959154fdc66caa9835426841 Mon Sep 17 00:00:00 2001
From: npentrel
Date: Wed, 21 May 2025 11:33:57 +0000
Subject: [PATCH 01/17] Documentation updates from Promptless
---
.../operate/reference/module-configuration.md | 62 ++-
docs/operate/reference/naming-modules.md | 40 +-
docs/operate/reference/single-page-apps.md | 198 +++++++
.../projects/create-single-page-app.md | 502 ++++++++++++++++++
4 files changed, 800 insertions(+), 2 deletions(-)
create mode 100644 docs/operate/reference/single-page-apps.md
create mode 100644 docs/tutorials/projects/create-single-page-app.md
diff --git a/docs/operate/reference/module-configuration.md b/docs/operate/reference/module-configuration.md
index fba7b1288f..e2c0a735a9 100644
--- a/docs/operate/reference/module-configuration.md
+++ b/docs/operate/reference/module-configuration.md
@@ -179,7 +179,7 @@ The model is configured as a component with the name `myRealsenseCamera1`.
```
{{% /tab %}}
-{{% /tabs %}}
+{{< /tabs %}}
{{% /tab %}}
{{< /tabs >}}
@@ -227,8 +227,68 @@ For any version type other than **Patch (X.Y.Z)**, the module will upgrade as so
If, for example, the module provides a motor component, and the motor is running, it will stop while the module upgrades.
{{% /alert %}}
+### Module meta.json configuration
+
+When creating a module, you'll need to configure a `meta.json` file that defines the module's properties. This file includes information about the module's ID, visibility, models, and other features.
+
+Here's an example of a `meta.json` file:
+
+```json
+{
+ "module_id": "your-namespace:your-module",
+ "visibility": "public",
+ "url": "https://github.com/your-org/your-repo",
+ "description": "Your module description",
+ "models": [
+ {
+ "api": "rdk:component:base",
+ "model": "your-namespace:your-module:your-model"
+ }
+ ],
+ "entrypoint": "run.sh",
+ "first_run": "setup.sh"
+}
+```
+
+For modules that include [Single Page Apps](/operate/reference/single-page-apps/), you can add the `applications` field:
+
+```json
+{
+ "module_id": "your-namespace:your-module",
+ "visibility": "public",
+ "url": "https://github.com/your-org/your-repo",
+ "description": "Your module description",
+ "models": [
+ {
+ "api": "rdk:component:base",
+ "model": "your-namespace:your-module:your-model"
+ }
+ ],
+ "entrypoint": "run.sh",
+ "applications": [
+ {
+ "name": "your-app-name",
+ "type": "web",
+ "entrypoint": "dist/index.html"
+ }
+ ]
+}
+```
+
+The `applications` field is an array of application objects with the following properties:
+
+| Property | Type | Description |
+| ------------ | ------ | ------------------------------------------------------------------------------------------------- |
+| `name` | string | The name of your application, which will be used in the URL (`name.publicnamespace.viamapps.com`) |
+| `type` | string | The type of application (currently only `"web"` is supported) |
+| `entrypoint` | string | The path to the HTML entry point for your application |
+
+For more information about Single Page Apps, see the [Single Page Apps documentation](/operate/reference/single-page-apps/).
+
### Environment variables
+#### Environment variables
+
Each module has access to the following default environment variables.
Not all of these variables are automatically available on [local modules](/operate/get-started/other-hardware/#test-your-module-locally); you can manually set variables your module requires if necessary.
diff --git a/docs/operate/reference/naming-modules.md b/docs/operate/reference/naming-modules.md
index bd544e60e1..9f95098182 100644
--- a/docs/operate/reference/naming-modules.md
+++ b/docs/operate/reference/naming-modules.md
@@ -95,10 +95,48 @@ More requirements:
Determine the model name you want to use based on these requirements, then proceed to the next section.
+## Valid application identifiers
+
+If your module includes [Single Page Apps](/operate/reference/single-page-apps/), you'll need to define application names in your module's `meta.json` file. Application names have the following requirements:
+
+- Application names must be all-lowercase.
+- Application names may only use alphanumeric (`a-z` and `0-9`), hyphen (`-`), and underscore (`_`) characters.
+- Application names must be unique within your organization's namespace.
+
+The application name will be used in the URL for accessing your application:
+
+```
+https://app-name.your-public-namespace.viamapps.com
+```
+
+For example, if your organization namespace is `acme` and your application name is `dashboard`, your application will be accessible at:
+
+```
+https://dashboard.acme.viamapps.com
+```
+
+Here's an example of how to define applications in your module's `meta.json` file:
+
+```json
+{
+ "module_id": "acme:my-module",
+ "visibility": "public",
+ "applications": [
+ {
+ "name": "dashboard",
+ "type": "web",
+ "entrypoint": "dist/index.html"
+ }
+ ]
+}
+```
+
+For more information about Single Page Apps, see the [Single Page Apps documentation](/operate/reference/single-page-apps/).
+
## Create a namespace for your organization
When uploading modules to the Viam Registry, you must set a unique namespace for your organization to associate your module with.
To create a new namespace for your organization, click on the org's **Settings** in the top right of the navigation bar, then click the **Set a public namespace** button.
Enter a name or use the suggested name for your namespace, and then click **Set namespace**.
-A namespace may only contain letters, numbers, and the dash (`-`) character.
+A namespace may only contain letters, numbers, and the dash (`-`) character.
\ No newline at end of file
diff --git a/docs/operate/reference/single-page-apps.md b/docs/operate/reference/single-page-apps.md
new file mode 100644
index 0000000000..d09e84c51c
--- /dev/null
+++ b/docs/operate/reference/single-page-apps.md
@@ -0,0 +1,198 @@
+---
+title: "Single Page Apps"
+linkTitle: "Single Page Apps"
+weight: 45
+no_list: true
+type: docs
+icon: true
+description: "Create and deploy single page applications hosted by Viam."
+---
+
+Viam Single Page Apps allow you to upload a single page application and access it via a dedicated URL (`appname.publicnamespace.viamapps.com`), with hosting, authentication, and boilerplate logic handled for you. This feature enables you to create custom web interfaces for your machines without having to manage the infrastructure for hosting and authentication.
+
+## Overview
+
+With Viam Single Page Apps, you can:
+
+- Host shared logic for users to choose an organization, location, and single machine
+- Render your application once a user has selected a machine
+- Store robot owner API key and secret in browser storage
+- Proxy requests through the viamapps.com domain to maintain same-origin across pages
+- Authenticate users via FusionAuth
+- Create and upload apps using the modules framework
+
+## Requirements
+
+To use Viam Single Page Apps, you need:
+
+- A module with a public visibility setting
+- A properly configured `meta.json` file with application information
+- A built single page application with an HTML entry point
+
+## Creating a Single Page App
+
+### 1. Build your application
+
+Build your single page application using your preferred framework (React, Vue, Angular, etc.). Your application should be built and ready for deployment, with all assets compiled into a distributable format.
+
+### 2. Configure your module's meta.json
+
+Add the `applications` field to your module's `meta.json` file to define your single page app:
+
+```json
+{
+ "module_id": "your-namespace:your-module",
+ "visibility": "public",
+ "url": "https://github.com/your-org/your-repo",
+ "description": "Your module description",
+ "models": [
+ {
+ "api": "rdk:component:base",
+ "model": "your-namespace:your-module:your-model"
+ }
+ ],
+ "entrypoint": "run.sh",
+ "applications": [
+ {
+ "name": "your-app-name",
+ "type": "web",
+ "entrypoint": "dist/index.html"
+ }
+ ]
+}
+```
+
+The `applications` field is an array of application objects with the following properties:
+
+| Property | Type | Description |
+| --- | --- | --- |
+| `name` | string | The name of your application, which will be used in the URL (`name.publicnamespace.viamapps.com`) |
+| `type` | string | The type of application (currently only `"web"` is supported) |
+| `entrypoint` | string | The path to the HTML entry point for your application |
+
+#### Entrypoint examples
+
+The `entrypoint` field specifies the path to your application's entry point. Here are some examples:
+
+- `"dist/index.html"` - Static content rooted at the `dist` directory
+- `"dist/foo.html"` - Static content rooted at the `dist` directory, with `foo.html` as the entry point
+- `"dist/"` - Static content rooted at the `dist` directory (assumes `dist/index.html` exists)
+- `"dist/bar/foo.html"` - Static content rooted at `dist/bar` with `foo.html` as the entry point
+
+### 3. Upload your module
+
+Package your module with your application included and upload it to the Viam Registry:
+
+```bash
+viam module build local
+viam module upload module.tar.gz
+```
+
+## Accessing your Single Page App
+
+Once your module with the application configuration is uploaded and approved, your application will be available at:
+
+```
+https://your-app-name.your-public-namespace.viamapps.com
+```
+
+Users will be prompted to authenticate with their Viam credentials before accessing your application.
+
+## Application Flow
+
+1. User navigates to `your-app-name.your-public-namespace.viamapps.com`
+2. User authenticates with Viam credentials
+3. User selects an organization, location, and machine
+4. User is redirected to `your-app-name.your-public-namespace.viamapps.com/machine/{machine-id}`
+5. Your application is rendered with access to the selected machine
+
+## API Changes
+
+The Single Page Apps feature introduces the following API changes:
+
+### App Object
+
+```protobuf
+message App {
+ string name = 1;
+ string type = 2;
+ string entrypoint = 3;
+}
+```
+
+### UpdateModuleRequest
+
+```protobuf
+message UpdateModuleRequest {
+ string module_id = 1;
+ Visibility visibility = 2;
+ string url = 3;
+ string description = 4;
+ repeated Model models = 5;
+ string entrypoint = 6;
+ optional string first_run = 7;
+ repeated App apps = 8;
+}
+```
+
+### GetAppContentRequest/Response
+
+```protobuf
+message GetAppContentRequest {
+ string public_namespace = 1;
+ string name = 2;
+}
+
+message GetAppContentResponse {
+ string url = 1;
+}
+```
+
+## Limitations
+
+- Single Page Apps currently only support single-machine applications
+- All modules with apps must have public visibility
+- There is no versioning or separate deploy step; the page will always render the latest version
+- Browsers with cookies disabled are not supported
+
+## Security Considerations
+
+- Customer apps are stored in GCS buckets that are publicly available on the internet
+- Avoid uploading sensitive information in your application code or assets
+- API keys and secrets are stored in the browser's localStorage or sessionStorage
+
+## Example
+
+Here's a complete example of creating and uploading a simple React application as a Viam Single Page App:
+
+```bash
+# Create a new React app
+npx create-react-app my-viam-app
+cd my-viam-app
+
+# Build the app
+npm run build
+
+# Create a module with the app
+viam module create --name my-viam-app --public-namespace your-namespace
+
+# Edit meta.json to add the application configuration
+# Add the following to meta.json:
+# "visibility": "public",
+# "applications": [
+# {
+# "name": "my-app",
+# "type": "web",
+# "entrypoint": "build/index.html"
+# }
+# ]
+
+# Copy the build directory to the module directory
+cp -r build/ path/to/module/
+
+# Build and upload the module
+viam module build local
+viam module upload module.tar.gz
+```
+
+After the module is approved, your application will be available at `https://my-app.your-public-namespace.viamapps.com`.
\ No newline at end of file
diff --git a/docs/tutorials/projects/create-single-page-app.md b/docs/tutorials/projects/create-single-page-app.md
new file mode 100644
index 0000000000..f775f2402e
--- /dev/null
+++ b/docs/tutorials/projects/create-single-page-app.md
@@ -0,0 +1,502 @@
+---
+title: "Create a Single Page App"
+linkTitle: "Create a Single Page App"
+weight: 45
+type: "docs"
+description: "Build and deploy a web application hosted by Viam."
+tags: ["web", "single page app", "react", "module"]
+difficulty: intermediate
+time_to_complete: "1 hour"
+---
+
+This tutorial guides you through creating and deploying a Single Page App (SPA) with Viam. You'll build a simple React application that allows users to select a machine and view its camera feed.
+
+## Prerequisites
+
+- A Viam account with an organization that has a public namespace
+- Node.js and npm installed on your development machine
+- Basic familiarity with React
+- A machine with a camera component configured in your Viam account
+
+## Step 1: Create a React application
+
+First, let's create a new React application using Create React App:
+
+```bash
+npx create-react-app viam-camera-viewer
+cd viam-camera-viewer
+```
+
+## Step 2: Install dependencies
+
+Install the Viam TypeScript SDK and other necessary dependencies:
+
+```bash
+npm install @viamrobotics/sdk react-router-dom
+```
+
+## Step 3: Create the application structure
+
+Let's create a simple application with the following pages:
+
+1. Home page - For selecting organization, location, and machine
+2. Machine page - For viewing the selected machine's camera feed
+
+Create the following folder structure:
+
+```
+src/
+├── components/
+│ ├── OrganizationSelector.js
+│ ├── LocationSelector.js
+│ ├── MachineSelector.js
+│ └── CameraViewer.js
+├── pages/
+│ ├── HomePage.js
+│ └── MachinePage.js
+├── App.js
+└── index.js
+```
+
+## Step 4: Implement the components
+
+Let's implement each component:
+
+### OrganizationSelector.js
+
+```jsx
+import React, { useState, useEffect } from 'react';
+
+function OrganizationSelector({ onSelect }) {
+ const [organizations, setOrganizations] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ // In a real app, you would fetch organizations from the Viam API
+ // This is a simplified example
+ setOrganizations([
+ { id: 'org1', name: 'My Organization' },
+ { id: 'org2', name: 'Another Organization' }
+ ]);
+ setLoading(false);
+ }, []);
+
+ if (loading) return
Loading organizations...
;
+ if (error) return
Error: {error}
;
+
+ return (
+
+
Select an Organization
+
+
+ );
+}
+
+export default OrganizationSelector;
+```
+
+### LocationSelector.js
+
+```jsx
+import React, { useState, useEffect } from 'react';
+
+function LocationSelector({ organizationId, onSelect }) {
+ const [locations, setLocations] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ if (!organizationId) {
+ setLocations([]);
+ setLoading(false);
+ return;
+ }
+
+ // In a real app, you would fetch locations from the Viam API
+ // This is a simplified example
+ setLocations([
+ { id: 'loc1', name: 'Home' },
+ { id: 'loc2', name: 'Office' }
+ ]);
+ setLoading(false);
+ }, [organizationId]);
+
+ if (!organizationId) return null;
+ if (loading) return
Loading locations...
;
+ if (error) return
Error: {error}
;
+
+ return (
+
+
Select a Location
+
+
+ );
+}
+
+export default LocationSelector;
+```
+
+### MachineSelector.js
+
+```jsx
+import React, { useState, useEffect } from 'react';
+import { useNavigate } from 'react-router-dom';
+
+function MachineSelector({ locationId }) {
+ const [machines, setMachines] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const navigate = useNavigate();
+
+ useEffect(() => {
+ if (!locationId) {
+ setMachines([]);
+ setLoading(false);
+ return;
+ }
+
+ // In a real app, you would fetch machines from the Viam API
+ // This is a simplified example
+ setMachines([
+ { id: 'machine1', name: 'My Robot' },
+ { id: 'machine2', name: 'Another Robot' }
+ ]);
+ setLoading(false);
+ }, [locationId]);
+
+ const handleMachineSelect = (machineId) => {
+ if (machineId) {
+ navigate(`/machine/${machineId}`);
+ }
+ };
+
+ if (!locationId) return null;
+ if (loading) return
Loading machines...
;
+ if (error) return
Error: {error}
;
+
+ return (
+
+
Select a Machine
+
+
+ );
+}
+
+export default MachineSelector;
+```
+
+### CameraViewer.js
+
+```jsx
+import React, { useState, useEffect, useRef } from 'react';
+
+function CameraViewer({ machineId }) {
+ const [cameras, setCameras] = useState([]);
+ const [selectedCamera, setSelectedCamera] = useState('');
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const imageRef = useRef(null);
+
+ useEffect(() => {
+ if (!machineId) {
+ setCameras([]);
+ setLoading(false);
+ return;
+ }
+
+ // In a real app, you would fetch cameras from the Viam API
+ // This is a simplified example
+ setCameras([
+ { id: 'camera1', name: 'Main Camera' },
+ { id: 'camera2', name: 'Secondary Camera' }
+ ]);
+ setLoading(false);
+ }, [machineId]);
+
+ useEffect(() => {
+ if (!selectedCamera) return;
+
+ // In a real app, you would set up a stream from the Viam API
+ // This is a simplified example
+ const interval = setInterval(() => {
+ // Simulate camera feed with a placeholder
+ if (imageRef.current) {
+ imageRef.current.src = `https://picsum.photos/800/600?random=${Date.now()}`;
+ }
+ }, 1000);
+
+ return () => clearInterval(interval);
+ }, [selectedCamera]);
+
+ const handleCameraSelect = (cameraId) => {
+ setSelectedCamera(cameraId);
+ };
+
+ if (!machineId) return null;
+ if (loading) return
+ );
+}
+
+export default MachinePage;
+```
+
+## Step 6: Update App.js with routing
+
+```jsx
+import React from 'react';
+import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
+import HomePage from './pages/HomePage';
+import MachinePage from './pages/MachinePage';
+
+function App() {
+ return (
+
+
+ } />
+ } />
+
+
+ );
+}
+
+export default App;
+```
+
+## Step 7: Build the application
+
+Build your React application:
+
+```bash
+npm run build
+```
+
+This will create a production build in the `build` directory.
+
+## Step 8: Create a Viam module
+
+Now, let's create a Viam module to host our Single Page App:
+
+```bash
+mkdir viam-camera-viewer-module
+cd viam-camera-viewer-module
+```
+
+Create a `meta.json` file:
+
+```json
+{
+ "module_id": "your-namespace:camera-viewer",
+ "visibility": "public",
+ "url": "https://github.com/your-username/viam-camera-viewer",
+ "description": "A simple camera viewer single page application",
+ "models": [],
+ "entrypoint": "",
+ "applications": [
+ {
+ "name": "camera-viewer",
+ "type": "web",
+ "entrypoint": "build/index.html"
+ }
+ ]
+}
+```
+
+Replace `your-namespace` with your organization's public namespace.
+
+## Step 9: Package and upload the module
+
+Copy your React build files to the module directory:
+
+```bash
+cp -r ../viam-camera-viewer/build ./
+```
+
+Create a module tarball:
+
+```bash
+tar -czvf module.tar.gz build meta.json
+```
+
+Upload the module to the Viam Registry:
+
+```bash
+viam module upload module.tar.gz
+```
+
+## Step 10: Access your Single Page App
+
+Once your module is approved, you can access your Single Page App at:
+
+```
+https://camera-viewer.your-namespace.viamapps.com
+```
+
+Replace `your-namespace` with your organization's public namespace.
+
+## Integrating with the Viam SDK
+
+The example above uses placeholder data. In a real application, you would use the Viam TypeScript SDK to interact with your machines. Here's how you might implement the actual API calls:
+
+### Connecting to Viam
+
+```jsx
+import { createRobotClient, ViamClient } from '@viamrobotics/sdk';
+
+// Create a Viam client
+const createClient = async () => {
+ try {
+ // Get credentials from localStorage
+ const apiKey = localStorage.getItem('viamApiKey');
+ const apiKeyId = localStorage.getItem('viamApiKeyId');
+
+ if (!apiKey || !apiKeyId) {
+ throw new Error('API credentials not found');
+ }
+
+ const client = await createRobotClient({
+ host: 'your-machine-fqdn.viam.cloud',
+ credential: {
+ type: 'api-key',
+ payload: apiKey,
+ },
+ signingAuthority: apiKeyId,
+ authEntity: 'your-machine-id',
+ });
+
+ return client;
+ } catch (error) {
+ console.error('Error creating client:', error);
+ throw error;
+ }
+};
+```
+
+### Getting camera data
+
+```jsx
+// Get camera image
+const getCameraImage = async (client, cameraName) => {
+ try {
+ const camera = await client.camera.fromRobot(cameraName);
+ const image = await camera.getImage();
+ return image;
+ } catch (error) {
+ console.error('Error getting camera image:', error);
+ throw error;
+ }
+};
+```
+
+## Conclusion
+
+You've successfully created and deployed a Single Page App with Viam! This simple application demonstrates how to build a web interface for interacting with Viam machines. You can extend this application to include more features, such as controlling motors, reading sensor data, or implementing more complex machine interactions.
+
+For more information about Single Page Apps, see the [Single Page Apps documentation](/operate/reference/single-page-apps/).
\ No newline at end of file
From aa38d5122c9d9046e1b74b143ab47b32e7470afa Mon Sep 17 00:00:00 2001
From: Naomi Pentrel <5212232+npentrel@users.noreply.github.com>
Date: Wed, 21 May 2025 18:49:43 +0200
Subject: [PATCH 02/17] Update
---
docs/operate/control/single-page-apps.md | 200 ++++++++++++++++++
.../get-started/other-hardware/_index.md | 16 +-
.../operate/reference/module-configuration.md | 4 +-
docs/operate/reference/naming-modules.md | 30 +--
docs/operate/reference/single-page-apps.md | 198 -----------------
5 files changed, 220 insertions(+), 228 deletions(-)
create mode 100644 docs/operate/control/single-page-apps.md
delete mode 100644 docs/operate/reference/single-page-apps.md
diff --git a/docs/operate/control/single-page-apps.md b/docs/operate/control/single-page-apps.md
new file mode 100644
index 0000000000..186e06eecd
--- /dev/null
+++ b/docs/operate/control/single-page-apps.md
@@ -0,0 +1,200 @@
+---
+title: "Create a custom web interface"
+linkTitle: "Create a custom web interface"
+weight: 11
+no_list: true
+type: docs
+description: "Create and deploy single page applications on the Viam platform."
+---
+
+With single page apps you can create and deploy custom web interfaces for your machines that use a single HTML page.
+Single page apps are accessible from a dedicated URL (`appname.publicnamespace.viamapps.com`) and hosting and authentication is handled for you.
+
+When opening an app, users log in and then select a machine they have access to.
+Then your app is rendered and ready for use.
+
+TODO: Example GIF
+
+## 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 >}}
+
+## Create a single page app
+
+{{< table >}}
+{{% tablestep number=1 %}}
+
+**Build your single page application** using your preferred framework like React, Vue, Angular, or others.
+Your application should be built and ready for deployment, with all assets compiled into a distributable format.
+
+TODO: cover dev process
+TODO: how do you connect to the machine / how do you access the api key?
+
+{{% /tablestep %}}
+{{% tablestep number=2 %}}
+
+**Create a meta.json** file 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": "web",
+ "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": "web",
+ "entrypoint": "dist/index.html"
+ }
+ ]
+}
+```
+
+{{% /tab %}}
+{{< /tabs >}}
+
+{{% expand "Click to view" %}}
+
+
+| 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 [single page apps](/operate/reference/single-page-apps/) 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:
+
+
+| Property | Type | Description |
+| ------------ | ------ | ------------------------------------------------------------------------------------------------- |
+| `name` | string | The name of your application, which will be a part of the app's URL (`name.publicnamespace.viamapps.com`). For more information on valid names see [](/operate/reference/naming-modules). |
+| `type` | string | The type of application (currently only `"web"` 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:
"dist/index.html": Static content rooted at the `dist` directory
"dist/foo.html": Static content rooted at the `dist` directory, with `foo.html` as the entry point
"dist/": Static content rooted at the `dist` directory (assumes `dist/index.html` exists)
"dist/bar/foo.html": Static content rooted at `dist/bar` with `foo.html` as the entry point
|
+
+{{% /tablestep %}}
+{{% tablestep number=3 %}}
+
+**Package your app into a module and upload it** to the Viam Registry:
+
+TODO: first command doesn't make sense
+
+```sh {class="command-line" data-prompt="$" data-output="3-10"}
+viam module build local
+viam module upload module.tar.gz
+```
+
+TODO: the upload command requires platform & version - is that no longer the case?
+
+For subsequent updates you can use:
+
+```sh {class="command-line" data-prompt="$" data-output="2-10"}
+viam module update
+```
+
+{{% /tablestep %}}
+{{< /table >}}
+
+## Accessing your Single Page App
+
+Once your module with the application configuration is uploaded, your application will be available at:
+
+TODO: any extra steps?
+
+```
+https://your-app-name.your-public-namespace.viamapps.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.viamapps.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.viamapps.com/machine/{machine-id}`
+1. Your application is rendered with access to the selected machine
+
+## Limitations
+
+- Single page apps currently only support single-machine applications
+- All modules with apps must have public visibility
+- There is no versioning or separate deploy step; the page will always render the latest version
+- Browsers with cookies disabled are not supported
+
+## Security Considerations
+
+- Customer apps are stored in GCS buckets that are publicly available on the internet
+- Avoid uploading sensitive information in your application code or assets
+- API keys and secrets are stored in the browser's localStorage or sessionStorage
+- Single page apps authenticate users with FusionAuth
+
+## Example
+
+Here's a complete example of creating and uploading a simple React application as a Viam Single Page App:
+
+```bash
+# Create a new React app
+npx create-react-app my-viam-app
+cd my-viam-app
+
+# Build the app
+npm run build
+
+# Create a module with the app
+viam module create --name my-viam-app --public-namespace your-namespace
+
+# Edit meta.json to add the application configuration
+# Add the following to meta.json:
+# "visibility": "public",
+# "applications": [
+# {
+# "name": "my-app",
+# "type": "web",
+# "entrypoint": "build/index.html"
+# }
+# ]
+
+# Copy the build directory to the module directory
+cp -r build/ path/to/module/
+
+# Build and upload the module
+viam module build local
+viam module upload module.tar.gz
+```
+
+After the module is approved, your application will be available at `https://my-app.your-public-namespace.viamapps.com`.
diff --git a/docs/operate/get-started/other-hardware/_index.md b/docs/operate/get-started/other-hardware/_index.md
index efd365648a..7b439f0bca 100644
--- a/docs/operate/get-started/other-hardware/_index.md
+++ b/docs/operate/get-started/other-hardware/_index.md
@@ -987,16 +987,16 @@ Do not change the module_id.
models
-
object
-
Required
-
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 api and model key pair. If you are publishing a public module ("visibility": "public"), the namespace of your model must match the namespace of your organization.
+
array
+
Optional
+
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 api and model key pair. If you are publishing a public module ("visibility": "public"), the namespace of your model must match the namespace of your organization.
You are strongly encouraged to include a markdown_link 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, "README.md#configure-your-meteo_pm-sensor". Please also include a short_description describing what hardware the model supports.
entrypoint
string
-
Required
-
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 upload command, provide the path to that single file. If you are providing a directory containing your module to the upload command, provide the path to the entry point file contained within that directory.
+
Optional
+
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 upload command, provide the path to that single file. If you are providing a directory containing your module to the upload command, provide the path to the entry point file contained within that directory. Required if you are shipping a model.
build
@@ -1016,6 +1016,12 @@ Do not change the module_id.
Optional
Enables VS Code hover and autocomplete as you edit your module code. Gets auto-generated when you run viam module generate or viam module create. Has no impact on the module's function.
+
+
applications
+
array
+
Optional
+
Objects that provide information about the [Single Page Apps](/operate/reference/single-page-apps/) associated with the module.
+
diff --git a/docs/operate/reference/module-configuration.md b/docs/operate/reference/module-configuration.md
index e2c0a735a9..6a1918aa6d 100644
--- a/docs/operate/reference/module-configuration.md
+++ b/docs/operate/reference/module-configuration.md
@@ -179,7 +179,7 @@ The model is configured as a component with the name `myRealsenseCamera1`.
```
{{% /tab %}}
-{{< /tabs %}}
+{{% /tabs %}}
{{% /tab %}}
{{< /tabs >}}
@@ -285,8 +285,6 @@ The `applications` field is an array of application objects with the following p
For more information about Single Page Apps, see the [Single Page Apps documentation](/operate/reference/single-page-apps/).
-### Environment variables
-
#### Environment variables
Each module has access to the following default environment variables.
diff --git a/docs/operate/reference/naming-modules.md b/docs/operate/reference/naming-modules.md
index 9f95098182..fcf9b68979 100644
--- a/docs/operate/reference/naming-modules.md
+++ b/docs/operate/reference/naming-modules.md
@@ -9,7 +9,7 @@ description: "Add support for a new component or service model by writing a modu
languages: ["c++"]
viamresources: []
platformarea: ["registry"]
-toc_hide: true
+toc_hide: false
---
Each modular resource has two associated triplets: an API namespace triplet to indicate which [API](/dev/reference/apis/) it implements, and a model namespace triplet to uniquely identify the modular resource {{< glossary_tooltip term_id="model" text="model" >}}.
@@ -97,15 +97,17 @@ Determine the model name you want to use based on these requirements, then proce
## Valid application identifiers
-If your module includes [Single Page Apps](/operate/reference/single-page-apps/), you'll need to define application names in your module's `meta.json` file. Application names have the following requirements:
+If your module includes a [single page app](/operate/reference/single-page-apps/), you need to define the application name in your module's `meta.json` file.
+Application names have the following requirements:
- Application names must be all-lowercase.
-- Application names may only use alphanumeric (`a-z` and `0-9`), hyphen (`-`), and underscore (`_`) characters.
+- Application names may only use alphanumeric (`a-z` and `0-9`) and hyphen (`-`) characters.
+- Application names may not start or end with a hyphen.
- Application names must be unique within your organization's namespace.
-The application name will be used in the URL for accessing your application:
+The URL for accessing your single page app will contain your application name:
-```
+```txt
https://app-name.your-public-namespace.viamapps.com
```
@@ -115,22 +117,6 @@ For example, if your organization namespace is `acme` and your application name
https://dashboard.acme.viamapps.com
```
-Here's an example of how to define applications in your module's `meta.json` file:
-
-```json
-{
- "module_id": "acme:my-module",
- "visibility": "public",
- "applications": [
- {
- "name": "dashboard",
- "type": "web",
- "entrypoint": "dist/index.html"
- }
- ]
-}
-```
-
For more information about Single Page Apps, see the [Single Page Apps documentation](/operate/reference/single-page-apps/).
## Create a namespace for your organization
@@ -139,4 +125,4 @@ When uploading modules to the Viam Registry, you must set a unique namespace for
To create a new namespace for your organization, click on the org's **Settings** in the top right of the navigation bar, then click the **Set a public namespace** button.
Enter a name or use the suggested name for your namespace, and then click **Set namespace**.
-A namespace may only contain letters, numbers, and the dash (`-`) character.
\ No newline at end of file
+A namespace may only contain letters, numbers, and the dash (`-`) character.
diff --git a/docs/operate/reference/single-page-apps.md b/docs/operate/reference/single-page-apps.md
deleted file mode 100644
index d09e84c51c..0000000000
--- a/docs/operate/reference/single-page-apps.md
+++ /dev/null
@@ -1,198 +0,0 @@
----
-title: "Single Page Apps"
-linkTitle: "Single Page Apps"
-weight: 45
-no_list: true
-type: docs
-icon: true
-description: "Create and deploy single page applications hosted by Viam."
----
-
-Viam Single Page Apps allow you to upload a single page application and access it via a dedicated URL (`appname.publicnamespace.viamapps.com`), with hosting, authentication, and boilerplate logic handled for you. This feature enables you to create custom web interfaces for your machines without having to manage the infrastructure for hosting and authentication.
-
-## Overview
-
-With Viam Single Page Apps, you can:
-
-- Host shared logic for users to choose an organization, location, and single machine
-- Render your application once a user has selected a machine
-- Store robot owner API key and secret in browser storage
-- Proxy requests through the viamapps.com domain to maintain same-origin across pages
-- Authenticate users via FusionAuth
-- Create and upload apps using the modules framework
-
-## Requirements
-
-To use Viam Single Page Apps, you need:
-
-- A module with a public visibility setting
-- A properly configured `meta.json` file with application information
-- A built single page application with an HTML entry point
-
-## Creating a Single Page App
-
-### 1. Build your application
-
-Build your single page application using your preferred framework (React, Vue, Angular, etc.). Your application should be built and ready for deployment, with all assets compiled into a distributable format.
-
-### 2. Configure your module's meta.json
-
-Add the `applications` field to your module's `meta.json` file to define your single page app:
-
-```json
-{
- "module_id": "your-namespace:your-module",
- "visibility": "public",
- "url": "https://github.com/your-org/your-repo",
- "description": "Your module description",
- "models": [
- {
- "api": "rdk:component:base",
- "model": "your-namespace:your-module:your-model"
- }
- ],
- "entrypoint": "run.sh",
- "applications": [
- {
- "name": "your-app-name",
- "type": "web",
- "entrypoint": "dist/index.html"
- }
- ]
-}
-```
-
-The `applications` field is an array of application objects with the following properties:
-
-| Property | Type | Description |
-| --- | --- | --- |
-| `name` | string | The name of your application, which will be used in the URL (`name.publicnamespace.viamapps.com`) |
-| `type` | string | The type of application (currently only `"web"` is supported) |
-| `entrypoint` | string | The path to the HTML entry point for your application |
-
-#### Entrypoint examples
-
-The `entrypoint` field specifies the path to your application's entry point. Here are some examples:
-
-- `"dist/index.html"` - Static content rooted at the `dist` directory
-- `"dist/foo.html"` - Static content rooted at the `dist` directory, with `foo.html` as the entry point
-- `"dist/"` - Static content rooted at the `dist` directory (assumes `dist/index.html` exists)
-- `"dist/bar/foo.html"` - Static content rooted at `dist/bar` with `foo.html` as the entry point
-
-### 3. Upload your module
-
-Package your module with your application included and upload it to the Viam Registry:
-
-```bash
-viam module build local
-viam module upload module.tar.gz
-```
-
-## Accessing your Single Page App
-
-Once your module with the application configuration is uploaded and approved, your application will be available at:
-
-```
-https://your-app-name.your-public-namespace.viamapps.com
-```
-
-Users will be prompted to authenticate with their Viam credentials before accessing your application.
-
-## Application Flow
-
-1. User navigates to `your-app-name.your-public-namespace.viamapps.com`
-2. User authenticates with Viam credentials
-3. User selects an organization, location, and machine
-4. User is redirected to `your-app-name.your-public-namespace.viamapps.com/machine/{machine-id}`
-5. Your application is rendered with access to the selected machine
-
-## API Changes
-
-The Single Page Apps feature introduces the following API changes:
-
-### App Object
-
-```protobuf
-message App {
- string name = 1;
- string type = 2;
- string entrypoint = 3;
-}
-```
-
-### UpdateModuleRequest
-
-```protobuf
-message UpdateModuleRequest {
- string module_id = 1;
- Visibility visibility = 2;
- string url = 3;
- string description = 4;
- repeated Model models = 5;
- string entrypoint = 6;
- optional string first_run = 7;
- repeated App apps = 8;
-}
-```
-
-### GetAppContentRequest/Response
-
-```protobuf
-message GetAppContentRequest {
- string public_namespace = 1;
- string name = 2;
-}
-
-message GetAppContentResponse {
- string url = 1;
-}
-```
-
-## Limitations
-
-- Single Page Apps currently only support single-machine applications
-- All modules with apps must have public visibility
-- There is no versioning or separate deploy step; the page will always render the latest version
-- Browsers with cookies disabled are not supported
-
-## Security Considerations
-
-- Customer apps are stored in GCS buckets that are publicly available on the internet
-- Avoid uploading sensitive information in your application code or assets
-- API keys and secrets are stored in the browser's localStorage or sessionStorage
-
-## Example
-
-Here's a complete example of creating and uploading a simple React application as a Viam Single Page App:
-
-```bash
-# Create a new React app
-npx create-react-app my-viam-app
-cd my-viam-app
-
-# Build the app
-npm run build
-
-# Create a module with the app
-viam module create --name my-viam-app --public-namespace your-namespace
-
-# Edit meta.json to add the application configuration
-# Add the following to meta.json:
-# "visibility": "public",
-# "applications": [
-# {
-# "name": "my-app",
-# "type": "web",
-# "entrypoint": "build/index.html"
-# }
-# ]
-
-# Copy the build directory to the module directory
-cp -r build/ path/to/module/
-
-# Build and upload the module
-viam module build local
-viam module upload module.tar.gz
-```
-
-After the module is approved, your application will be available at `https://my-app.your-public-namespace.viamapps.com`.
\ No newline at end of file
From 14fad223fd80dd29da141f94ffbf55bef23dc54a Mon Sep 17 00:00:00 2001
From: Naomi Pentrel <5212232+npentrel@users.noreply.github.com>
Date: Fri, 23 May 2025 10:39:25 +0200
Subject: [PATCH 03/17] Draft
---
docs/operate/control/single-page-apps.md | 27 +++++++++++++++++-------
1 file changed, 19 insertions(+), 8 deletions(-)
diff --git a/docs/operate/control/single-page-apps.md b/docs/operate/control/single-page-apps.md
index 186e06eecd..9e8537dc51 100644
--- a/docs/operate/control/single-page-apps.md
+++ b/docs/operate/control/single-page-apps.md
@@ -7,8 +7,16 @@ type: docs
description: "Create and deploy single page applications on the Viam platform."
---
+- Deploy front-end apps
+-
+
+you can have multiple HTML files
+files have to all be deployed
+
With single page apps you can create and deploy custom web interfaces for your machines that use a single HTML page.
-Single page apps are accessible from a dedicated URL (`appname.publicnamespace.viamapps.com`) and hosting and authentication is handled for you.
+Single page apps are accessible from a dedicated URL (`appname_publicnamespace.viamapplications.com`) and hosting and authentication is handled for you.
+
+google SPA and include what exactly that means
When opening an app, users log in and then select a machine they have access to.
Then your app is rendered and ready for use.
@@ -56,7 +64,7 @@ TODO: how do you connect to the machine / how do you access the api key?
"applications": [
{
"name": "your-app-name",
- "type": "web",
+ "type": "single_machine",
"entrypoint": "dist/index.html"
}
]
@@ -75,7 +83,7 @@ TODO: how do you connect to the machine / how do you access the api key?
"applications": [
{
"name": "dashboard",
- "type": "web",
+ "type": "single_machine",
"entrypoint": "dist/index.html"
}
]
@@ -104,8 +112,8 @@ The `applications` field is an array of application objects with the following p
| Property | Type | Description |
| ------------ | ------ | ------------------------------------------------------------------------------------------------- |
-| `name` | string | The name of your application, which will be a part of the app's URL (`name.publicnamespace.viamapps.com`). For more information on valid names see [](/operate/reference/naming-modules). |
-| `type` | string | The type of application (currently only `"web"` is supported). |
+| `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 [](/operate/reference/naming-modules). |
+| `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:
"dist/index.html": Static content rooted at the `dist` directory
"dist/foo.html": Static content rooted at the `dist` directory, with `foo.html` as the entry point
"dist/": Static content rooted at the `dist` directory (assumes `dist/index.html` exists)
"dist/bar/foo.html": Static content rooted at `dist/bar` with `foo.html` as the entry point
|
{{% /tablestep %}}
@@ -114,10 +122,11 @@ The `applications` field is an array of application objects with the following p
**Package your app into a module and upload it** to the Viam Registry:
TODO: first command doesn't make sense
+don't use module generate
```sh {class="command-line" data-prompt="$" data-output="3-10"}
viam module build local
-viam module upload module.tar.gz
+viam module upload module.tar.gz --platform=any
```
TODO: the upload command requires platform & version - is that no longer the case?
@@ -143,10 +152,10 @@ https://your-app-name.your-public-namespace.viamapps.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.viamapps.com`
+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.viamapps.com/machine/{machine-id}`
+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
## Limitations
@@ -198,3 +207,5 @@ viam module upload module.tar.gz
```
After the module is approved, your application will be available at `https://my-app.your-public-namespace.viamapps.com`.
+
+https://github.com/bashar-515/sample-app
From 660da6ec2b9375afe000798abcaf908788853f1e Mon Sep 17 00:00:00 2001
From: Naomi Pentrel <5212232+npentrel@users.noreply.github.com>
Date: Fri, 23 May 2025 22:23:44 +0200
Subject: [PATCH 04/17] Update
---
assets/operate/spa.png | Bin 0 -> 37636 bytes
.../air-quality-fleet/get-readings.png | Bin 103018 -> 39737 bytes
docs/operate/control/single-page-apps.md | 124 +-
docs/tutorials/control/air-quality-fleet.md | 1094 +++++++++--------
4 files changed, 642 insertions(+), 576 deletions(-)
create mode 100644 assets/operate/spa.png
diff --git a/assets/operate/spa.png b/assets/operate/spa.png
new file mode 100644
index 0000000000000000000000000000000000000000..c2f1282bc6ec602c46ed7ceb02dcb9b4ed780c8e
GIT binary patch
literal 37636
zcmc$`WmuGL7dA@AAc9B;2#ktS64K28lF~4ADcwkSil_)8DGkz%bdR9YT|+p8fW*+<
zd^gWy%=3Of_CEHX?QwwPeP6ZKRp&a_Ilfj=k|6|9fzZ&<2<2oStD&J`0-rIw@Nj@9
zuk5Nc(9l3tR+5q`a*~o@6(Bwgz7^Jhm;p@L
zO;2MBIX@aCgOS0hf}fHx?Xhd}JyJ8wfZH3qxpb>Z2Rx?tfm6KWLE;&XG@-K8&6=CT&
zqb<5JHkg^3I;q-APTbnR@llvE?fp?af2V&~0%LfmIl~iAFLrf_t)CY7!xJnQ{Z3~N
zow5}DMNeNa3rH={MMM->_Fl+#UyCURKjJridHkK}l1b^tsZ1{iLpO`x%Zj0U+FU(m
z-njDfS-7~pjwCZ$t6%!frTRG7yBVInluDQza*cj$*h6U^X6{U!DXD@Hs
zb8TrWGdR00TMDc1#6xcV?(