MOVED
Resources for Developers
Overview
Resources are files, plain text, or JSON stored alongside systems, devices, users, blueprints and other data objects in the Leverege IoT Stack. They can capture images, videos, and other binary data generated by or associated with devices over time, as well as textual information such as I18n files, json, cvs, and plain text. Resources enable applications to store visual records, support AI/ML inference workflows, and provide historical context for device activity.
Each resource belongs to a data object and has a unique path within that data object’s resource hierarchy. Resources are stored in Google Cloud Storage and tracked by the resource-server, which handles storage, retrieval, and lifecycle management.
When to Use Resources
Rule of thumb: If it’s a file that gets uploaded, captured, or downloaded, use a resource. If it’s structured data the system processes, use an attribute.
Resource Timestamps: createdAt vs dataCreatedAt
Resources have two timestamp fields that serve different purposes:
createdAt: When the resource record was created in the system dataCreatedAt: When the underlying data was actually captured or generated Why this matters: A camera might capture an image at 10:00 AM but upload it at 10:15 AM due to network delays. The dataCreatedAt would be 10:00 AM (when the image was taken), while createdAt would be 10:15 AM (when the system received it).
By default, resources are sorted by dataCreatedAt in the UI, showing them in capture order rather than upload order.
Resources vs Resource Pointers
A resource stores an actual file in Google Cloud Storage.
A resource pointer references another resource’s file without duplicating it. Pointers have their own metadata independent of the original resource.
Use resource pointers when:
Multiple devices need to reference the same image You want to annotate an image differently for different contexts Creating a curated subset of resources from a larger collection Important: If the original resource is deleted, the pointer will still exist but its file will be inaccessible.
Resource Query Parameters
Performance tip: Avoid high offset values when paginating large result sets. Use time-based filtering on dataCreatedAt or cursor-based pagination when possible.
BigQuery Integration
Resource changes are appended to a BigQuery table for analytics and historical queries:
Table: resource_server.resource
Every resource create, update, delete, or archive appends a new row. Deduplicate using updatedAt for the latest state.
Cost optimization: This table is partitioned by createdAt. Always include a createdAt filter in queries for maximum efficiency.
-- Example: Get latest state of resources for a device
SELECT * FROM `project.resource_server.resource`
WHERE ownerId = 'device-id-here'
AND createdAt >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
QUALIFY ROW_NUMBER() OVER (PARTITION BY id ORDER BY updatedAt DESC) = 1
Permissions
Resource access has an independent permission set from the resource owner (device, system, etc):
By default, viewing blueprint resources (like translations) is restricted. To grant access to all users for ui purposes:
// Grant ResourceViewer to all users for a system's resources
await api.system(systemId)
.resource('i18n')
.user('any')
.addRole({ name: 'ResourceViewer', projectId: 'imagine' });
// Grant ResourceViewer for each blueprint
const blueprints = await api.project(projectId).blueprints().list();
for (const bp of blueprints.items) {
await api.blueprint(bp.id)
.resource('i18n')
.user('any')
.addRole({ name: 'ResourceViewer', projectId: 'imagine' });
}
Configuration
Resource Server File Limits
Increase both values in service configmaps, or use writeable gcs url’s for larger uploads.
Deletion Behavior
When a device is deleted, its resources are archived by default rather than deleted. This:
Preserves files in Google Cloud Storage Can be overridden via url params Troubleshooting
“Request entity too large” Error
Cause: File exceeds BODY_SIZE_LIMIT
Fix: Increase limits in api-server and resource-server configmaps, or use writeable gcs url’s (preferred)
Resources Upload but Don’t Display
Causes:
Missing permissions (most common) Debug steps:
Check resource-server logs for errors Verify resource exists via API Confirm user has ResourceViewer role for aforementioned resource Access the signed URL directly to test file availability Resource Pointer Shows No Image
Causes:
Source resource was deleted (pointer becomes orphaned) Pointer path is incorrect Debug: Query the pointer’s details to inspect its source reference fields
“ECONNREFUSED” to Resource Server
Cause: resource-server pod is down or restarting
Fix: Check pod status in Kubernetes, review resource-server logs for crash reasons