Skip to Content
Desarrollo de apps IoTAlmacenamiento de archivos

Almacenamiento de archivos

Cada backend de datos de app viene con almacenamiento de objetos privado junto a sus tablas. Donde el backend de datos guarda tus filas de series temporales, el almacenamiento de archivos guarda todo lo que no cabe en una fila: fotogramas de cámara, informes en PDF, blobs de firmware, clips de audio, exportaciones.

Ambos están pensados para usarse juntos. Almacenar un archivo te devuelve una URL permanente, y el patrón previsto es escribir esa URL en una columna de tabla en el mismo acto — un widget de panel renderiza entonces la imagen sin ningún trabajo adicional:

info = await ironflock.files.put("part-1.jpg", jpeg_bytes, content_type="image/jpeg") await ironflock.publish_to_table("inspections", part_id="1", photo_url=info.url)

La API completa del cliente — lectura, listado, uso, enlaces para compartir, objetos grandes, códigos de error — está documentada en la referencia del SDK. Esta página cubre el lado de la plataforma: cómo se declara, se gobierna, se comparte y se opera el almacenamiento.

Cómo funciona

  1. Declara opcionalmente una sección files: en .ironflock/data-template.yml.
  2. Cuando un usuario instala tu app en un proyecto, IronFlock aprovisiona un área de almacenamiento privada para ella — exactamente igual que aprovisiona la base de datos del proyecto.
  3. Tu código edge almacena y lee objetos a través de la API files del SDK.
  4. Desinstalar la app elimina su almacenamiento por completo: cada objeto y cada credencial, igual que se elimina el esquema de la base de datos.

Al igual que la base de datos, el almacenamiento es por proyecto. La misma app instalada en dos proyectos obtiene dos áreas de almacenamiento totalmente separadas y, como desarrollador de la app, no tienes acceso a ninguna de ellas — los datos pertenecen al usuario que ejecuta tu app.

Cero configuración es una configuración válida: una app sin sección files: obtiene igualmente un espacio de nombres llamado default, de modo que files.put(...) funciona de fábrica para cualquier app.

Declarar almacenamiento en la plantilla de datos

La sección files: vive junto a data: en data-template.yml:

files: description: Camera frames and generated inspection reports. # Presupuesto de almacenamiento que la app SUGIERE para sí misma, en bytes (aquí 5 GiB). # El usuario del proyecto puede cambiarlo; su ajuste es el que se aplica. quotaBytes: 5368709120 namespaces: - name: frames description: Raw camera frames, one JPEG per inspected part. contentTypes: ["image/jpeg"] maxObjectBytes: 20971520 retention: { deleteAfter: 30 days } - name: reports description: Generated PDF inspection reports. contentTypes: ["application/pdf"] private: true

Espacios de nombres

Un espacio de nombres es un prefijo de clave que lleva asociada una política. No es un bucket de almacenamiento aparte — cada espacio de nombres de una app vive dentro de la única área de almacenamiento de la app, y el nombre del espacio de nombres simplemente se convierte en el primer segmento de la ruta de almacenamiento de cada objeto.

Esa distinción te dice cuándo declarar uno:

  • ¿Organizar archivos? Usa rutas de clave — 2026/03/part-1.jpg — dentro del espacio de nombres default. Las claves con estilo de carpeta son el caso normal.
  • ¿Reglas distintas para un conjunto de objetos? Declara un espacio de nombres. Las reglas son lo único que añade un espacio de nombres.

Las reglas que puede llevar un espacio de nombres:

CampoSignificado
nameLetras minúsculas, dígitos y guiones, empezando por una letra. sys, system, ironflock e ironflock-* están reservados
descriptionSe muestra a los usuarios y es legible por los agentes de IA
privateExcluye el espacio de nombres del acceso entre apps. Por defecto false (compartido), igual que las tablas
contentTypesTipos MIME permitidos, se admiten globs (image/*). Por defecto: cualquiera
maxObjectBytesObjeto individual más grande, hasta 5 GiB. Por defecto 100 MiB
retention.deleteAfterLos objetos más antiguos que esto se eliminan automáticamente (30 days, 2 weeks, 1 year, …)

El presupuesto de almacenamiento

quotaBytes se declara una sola vez para toda la app, no por espacio de nombres. Un espacio de nombres es solo un prefijo de clave, así que no hay nada contra lo que aplicar un presupuesto por prefijo — la cuota se aplica a la única área de almacenamiento de la app, y el propio almacén de objetos la hace cumplir en cada ruta de escritura, incluidas las subidas directas.

Existen dos números, y la diferencia es deliberada:

  • La cuota sugerida — lo que pide tu plantilla. Se aplica cuando la app se instala por primera vez.
  • La cuota aplicada — lo que ha establecido el usuario del proyecto. Una vez que un usuario cambia el presupuesto en los ajustes de almacenamiento de la app, gana su valor, y volver a desplegar tu app no lo restablece.

Si tu plantilla no dice nada, se aplica el valor por defecto de la plataforma (1 GiB para los backends de desarrollo, 10 GiB para los de producción).

Retención

Los objetos que superan la edad deleteAfter de su espacio de nombres se eliminan automáticamente — el análogo, en el lado de los archivos, de la política dropAfter de las tablas. La retención se ejecuta dentro del almacén de objetos allí donde el almacén la soporta, y como una tarea diaria de la plataforma donde no (appliances on-premises), de modo que la retención declarada se comporta igual en todas partes.

URLs permanentes y paneles

Cada objeto almacenado tiene una URL estable con la forma https://files.ironflock.com/f/<backend>/<namespace>/<key>. Tres propiedades la convierten en lo correcto para escribir en una columna de tabla:

  • Nunca caduca. La URL es una dirección pura; permanece válida durante toda la vida del objeto.
  • No es un enlace público. Cada petición pasa por un proxy de autenticación que comprueba que el solicitante ha iniciado sesión y tiene acceso READ sobre este backend de datos — se vuelve a comprobar en cada petición. Revocar el acceso de un usuario revoca de inmediato su capacidad de obtener cualquier archivo.
  • Se renderiza en un <img>. El navegador envía su cookie de sesión automáticamente, así que un widget de panel puede usar la URL en <img src>, <video src> o un enlace de descarga sin necesidad de JavaScript.

Para entregar un archivo a alguien fuera del proyecto, el share_url del SDK acuña en su lugar un enlace al portador con caducidad — consulta compartir objetos para saber cuándo usar cada uno.

Archivos grandes

Las transferencias de hasta 6 MiB viajan como una única llamada a través del sistema de mensajería. Cualquier cosa más grande se transporta directamente entre el dispositivo y el almacenamiento de objetos por HTTPS — el SDK cambia automáticamente, transmite desde y hacia el disco, y un archivo de varios gigabytes nunca tiene que caber en memoria. El tope de una sola subida es de 5 GiB.

La ruta directa requiere que el dispositivo alcance el host del almacenamiento de objetos (s3.ironflock.com), no solo el router de mensajes. Si un proxy de fábrica solo permite el router, las transferencias grandes fallan con el código explícito PRESIGN_UNREACHABLE en lugar de con un error genérico — y un reloj de dispositivo desfasado más de 15 minutos falla con CLOCK_SKEW, que es una invitación a revisar el NTP, no las credenciales.

Compartir archivos entre apps

El acceso a archivos entre apps se apoya en el mismo consentimiento que el acceso a tablas entre apps. Hay un único interruptor: cuando un usuario del proyecto concede a la app B acceso a los datos de la app A (el consentimiento data_access en los ajustes de la app), esa concesión cubre las tablas de A y los espacios de nombres de archivos no privados de A. Revocarla revoca ambos.

Lo que tu app controla como proveedora es la bandera private por espacio de nombres:

  • private: false (el valor por defecto) — las apps que tengan una concesión de acceso a datos pueden leerlo (nunca escribirlo).
  • private: true — el espacio de nombres es invisible para las demás apps, sin más, incluso con una concesión.

Esto refleja exactamente consumir datos de otras apps: los espacios de nombres y las tablas tienen el mismo valor por defecto. Nada se comparte sin la concesión del usuario del proyecto — private: solo limita lo que ve un lector que ya cuenta con el consentimiento, no es el consentimiento en sí.

La vista del usuario

Los usuarios del proyecto ven y gobiernan el almacenamiento de tu app en dos lugares:

  • La vista de Datos muestra una entrada Archivos junto a las tablas y vistas de cada app — un listado con búsqueda de cada objeto almacenado, con tamaño, tipo y fecha de modificación, y descarga por archivo.
  • Los ajustes de almacenamiento de la app muestran el uso (bytes y número de objetos), el presupuesto aplicado junto a la sugerencia de tu app, un control para cambiar el presupuesto y una acción Eliminar todos los archivos — el equivalente, en el lado de los archivos, de vaciar todas las tablas. La eliminación se confirma escribiendo el nombre de la app y no se puede deshacer.
  • El asistente de IA puede listar, buscar y leer estos archivos en nombre de un usuario. Se aplica la misma comprobación DATABACKEND/READ, por lo que nunca muestra un archivo que el usuario no pudiera abrir por sí mismo. Busca en las rutas de los archivos, no en su contenido, y lee archivos de texto, imágenes y PDF — los archivos comprimidos y otros formatos binarios no puede leerlos.

Como con las tablas, estos son los datos del usuario: puede inspeccionarlos, limitarlos y eliminarlos sin contar contigo.

Acceso directo por S3

Para todo lo que va más allá del SDK — un analista con DuckDB, una copia de seguridad nocturna con rclone, un pipeline de BI — un usuario del proyecto puede emitir credenciales S3 de solo lectura por app desde los ajustes de almacenamiento de la app.

Cada credencial está limitada al área de almacenamiento de esa única app: una credencial emitida para la app A no funciona para los archivos de la app B, estructuralmente — la política de almacenamiento de B simplemente nunca la nombra. Un proyecto puede tener varias credenciales por app (una por consumidor: un trabajo de CI, un script de copia de seguridad, un portátil), y revocar una deja las demás funcionando. Esa es también la historia de la rotación: si una clave se filtra, emite una segunda credencial, migra el consumidor a ella, revoca la primera — ningún otro consumidor se ve afectado.

El secreto se muestra una sola vez, en el momento de la creación. Emitirla requiere acceso de lectura al backend de datos de esa app — el mismo permiso que permite leer los archivos en primer lugar, de modo que la credencial nunca puede ampliar el alcance de nadie.

# rclone rclone config create myapp s3 provider=Other \ endpoint=https://s3.ironflock.com \ access_key_id=ifs-key-3317-x7k2m secret_access_key=<shown once> rclone ls myapp:if-1042-3317
# DuckDB CREATE SECRET (TYPE S3, KEY_ID 'ifs-key-3317-x7k2m', SECRET '<shown once>', ENDPOINT 's3.ironflock.com'); SELECT * FROM read_parquet('s3://if-1042-3317/exports/*.parquet');

Los ajustes de almacenamiento muestran el endpoint y el nombre exacto del bucket al que apuntar un cliente. Ten en cuenta que un listado de buckets de nivel superior (aws s3 ls sin argumentos) no devuelve nada por diseño — la credencial no posee ningún bucket; se le concede acceso a uno. Dirígete al bucket directamente, como arriba.

Appliances on-premises

El almacenamiento de archivos funciona de forma idéntica en un appliance on-premises, con tres diferencias que se derivan del diseño del appliance:

  • Los archivos se sirven en el mismo origen bajo la propia dirección del appliance (/files/...) — sin nombre DNS adicional, sin certificado adicional, y funciona en instalaciones de HTTP simple. Las URLs permanentes y <img src> se comportan exactamente igual que en la nube.
  • El backend de almacenamiento del appliance transmite las descargas de archivos a través de la plataforma en lugar de redirigir a un host de almacenamiento aparte, de modo que el servicio de almacenamiento nunca se expone como un segundo origen.
  • Las credenciales S3 directas no están disponibles en los appliances — el almacén de objetos embebido no puede expresar concesiones de acceso por credencial. La sección simplemente no aparece allí en los ajustes de almacenamiento. Todo lo demás, incluido el comportamiento de archivos grandes a través del SDK y la retención declarada, funciona igual.

Un appliance aislado (air-gapped) sirve todo el tráfico de archivos de forma local: los logos de las apps, las imágenes de los paneles y las descargas de archivos no necesitan conectividad a internet.

Límites de un vistazo

LímiteValorDe dónde proviene
Transferencia inline (una sola llamada)6 MiBSe informa en tiempo de ejecución; puede aumentarse en el servidor
Objeto individual100 MiB por defecto, 5 GiB máx.maxObjectBytes por espacio de nombres
Subida individual5 GiBTope de un solo PUT del almacén de objetos; la subida multiparte aún no está disponible
Presupuesto de almacenamiento1 GiB dev / 10 GiB prod por defectoSugerido por la plantilla, decidido por el usuario
Longitud y caracteres de claveUTF-8, rutas separadas por /Se rechazan ../, los caracteres de control y los prefijos reservados
Last updated on