Archiviazione dei File
Ogni data backend di un’app include un’archiviazione a oggetti privata accanto alle sue tabelle. Dove il data backend conserva le tue righe di serie temporali, l’archiviazione dei file conserva tutto ciò che non entra in una riga: fotogrammi di telecamere, report PDF, blob di firmware, clip audio, esportazioni.
I due sono pensati per essere usati insieme. Memorizzare un file ti restituisce un URL permanente, e il pattern previsto è scrivere quell’URL in una colonna di tabella nello stesso momento — un widget di una dashboard renderizza poi l’immagine senza ulteriore lavoro:
Python
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)L’API client completa — lettura, elenco, utilizzo, link di condivisione, oggetti di grandi dimensioni, codici di errore — è documentata nel riferimento dell’SDK. Questa pagina copre il lato piattaforma: come l’archiviazione viene dichiarata, governata, condivisa e gestita.
Come Funziona
- Facoltativamente, dichiara una sezione
files:in.ironflock/data-template.yml. - Quando un utente installa la tua app in un progetto, IronFlock effettua il provisioning di un’area di archiviazione privata per essa — esattamente come effettua il provisioning del database del progetto.
- Il tuo codice edge memorizza e legge gli oggetti attraverso l’API
filesdell’SDK. - Disinstallare l’app rimuove completamente la sua archiviazione: ogni oggetto e ogni credenziale, proprio come viene eliminato lo schema del database.
Come il database, l’archiviazione è per progetto. La stessa app installata in due progetti ottiene due aree di archiviazione completamente separate, e come sviluppatore dell’app non hai accesso a nessuna delle due — i dati appartengono all’utente che esegue la tua app.
Zero configurazione è una configurazione valida: un’app senza sezione files: ottiene comunque un namespace chiamato default, quindi files.put(...) funziona da subito per ogni app.
Dichiarare l’Archiviazione nel Data Template
La sezione files: risiede accanto a data: in data-template.yml:
files:
description: Camera frames and generated inspection reports.
# Budget di archiviazione che l'app SUGGERISCE per se stessa, in byte (qui 5 GiB).
# L'utente del progetto può sovrascriverlo; è la sua impostazione a essere applicata.
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: trueNamespace
Un namespace è un prefisso di chiave che porta con sé delle policy. Non è un bucket di archiviazione separato — ogni namespace di un’app risiede all’interno dell’unica area di archiviazione dell’app, e il nome del namespace diventa semplicemente il primo segmento del percorso di archiviazione di ciascun oggetto.
Questa distinzione ti dice quando dichiararne uno:
- Organizzare i file? Usa percorsi di chiave —
2026/03/part-1.jpg— all’interno del namespacedefault. Le chiavi in stile cartella sono il caso normale. - Regole diverse per un insieme di oggetti? Dichiara un namespace. Le regole sono l’unica cosa che un namespace aggiunge.
Le regole che un namespace può portare con sé:
| Campo | Significato |
|---|---|
name | Lettere minuscole, cifre e trattini, che iniziano con una lettera. sys, system, ironflock e ironflock-* sono riservati |
description | Mostrata agli utenti e leggibile dagli agenti AI |
private | Esclude il namespace dall’accesso tra app. Il valore predefinito è false (condiviso), esattamente come per le tabelle |
contentTypes | Tipi MIME ammessi, glob consentiti (image/*). Predefinito: qualsiasi |
maxObjectBytes | Oggetto singolo più grande, fino a 5 GiB. Predefinito 100 MiB |
retention.deleteAfter | Gli oggetti più vecchi di questo valore vengono eliminati automaticamente (30 days, 2 weeks, 1 year, …) |
Il Budget di Archiviazione
quotaBytes viene dichiarato una sola volta per l’intera app, non per namespace. Un namespace è soltanto un prefisso di chiave, quindi non c’è nulla su cui far valere un budget per prefisso — la quota si applica all’unica area di archiviazione dell’app, e l’archiviazione a oggetti stessa la fa rispettare su ogni percorso di scrittura, inclusi gli upload diretti.
Esistono due numeri, e la differenza è deliberata:
- La quota suggerita — ciò che il tuo template richiede. Applicata quando l’app viene installata per la prima volta.
- La quota applicata — ciò che l’utente del progetto ha impostato. Una volta che un utente modifica il budget nelle impostazioni di archiviazione dell’app, vince il suo valore, e ridistribuire la tua app non lo azzera.
Se il tuo template non specifica nulla, si applica il valore predefinito della piattaforma (1 GiB per i backend di sviluppo, 10 GiB per quelli di produzione).
Conservazione
Gli oggetti che superano l’età deleteAfter del loro namespace vengono rimossi automaticamente — l’analogo lato file della policy dropAfter delle tabelle. La conservazione viene eseguita all’interno dell’archiviazione a oggetti dove questa la supporta, e come job giornaliero della piattaforma dove non la supporta (appliance on-premises), così la conservazione dichiarata si comporta allo stesso modo ovunque.
URL Permanenti e Dashboard
Ogni oggetto memorizzato ha un URL stabile della forma https://files.ironflock.com/f/<backend>/<namespace>/<key>. Tre proprietà lo rendono la cosa giusta da scrivere in una colonna di tabella:
- Non scade mai. L’URL è un puro indirizzo; resta valido per tutta la vita dell’oggetto.
- Non è un link pubblico. Ogni richiesta passa attraverso un proxy di autenticazione che verifica che il richiedente sia autenticato e possieda l’accesso READ su questo data backend — riverificato a ogni singola richiesta. Revocare l’accesso di un utente revoca immediatamente la sua capacità di recuperare qualsiasi file.
- Si renderizza in un
<img>. Il browser invia automaticamente il proprio cookie di sessione, quindi un widget di una dashboard può usare l’URL in<img src>,<video src>o un link di download senza alcun JavaScript coinvolto.
Per consegnare un file a qualcuno al di fuori del progetto, share_url dell’SDK genera invece un link al portatore a scadenza — vedi condividere gli oggetti per sapere quando usare l’uno o l’altro.
File di Grandi Dimensioni
I trasferimenti fino a 6 MiB viaggiano come una singola chiamata attraverso il sistema di messaggistica. Tutto ciò che è più grande viene trasportato direttamente tra il dispositivo e l’archiviazione a oggetti tramite HTTPS — l’SDK passa automaticamente all’altra modalità, trasmette in streaming da e verso il disco, e un file di più gigabyte non deve mai entrare in memoria. Il tetto massimo per l’upload singolo è 5 GiB.
Il percorso diretto richiede che il dispositivo raggiunga l’host dell’archiviazione a oggetti (s3.ironflock.com), non solo il router dei messaggi. Se un proxy di fabbrica consente soltanto il router, i trasferimenti di grandi dimensioni falliscono con il codice esplicito PRESIGN_UNREACHABLE invece che con un errore generico — e un orologio del dispositivo sfasato di più di 15 minuti fallisce con CLOCK_SKEW, che è un invito a controllare l’NTP, non le credenziali.
Condividere File tra App
L’accesso ai file tra app si appoggia sullo stesso consenso dell’accesso alle tabelle tra app. C’è un unico interruttore: quando un utente del progetto concede all’app B l’accesso ai dati dell’app A (il consenso data_access nelle impostazioni dell’app), quella concessione copre le tabelle di A e i namespace di file non privati di A. Revocarla revoca entrambi.
Ciò che la tua app controlla in quanto fornitrice è il flag private per namespace:
private: false(il valore predefinito) — le app che possiedono una concessione di accesso ai dati possono leggerlo (mai scriverlo).private: true— il namespace è invisibile alle altre app, punto e basta, anche in presenza di una concessione.
Questo rispecchia esattamente consumare dati da altre app: namespace e tabelle hanno lo stesso valore predefinito. Nulla viene condiviso senza la concessione dell’utente del progetto: private: restringe soltanto ciò che vede un lettore già autorizzato, non è il consenso stesso.
La Vista dell’Utente
Gli utenti del progetto vedono e governano l’archiviazione della tua app in due punti:
- La vista Dati mostra una voce File accanto alle tabelle e alle viste di ogni app — un elenco ricercabile di ogni oggetto memorizzato con dimensione, tipo e data di modifica, e download per singolo file.
- Le impostazioni di archiviazione dell’app mostrano l’utilizzo (byte e numero di oggetti), il budget applicato accanto al suggerimento della tua app, un controllo per modificare il budget, e un’azione Elimina tutti i file — il gemello lato file dello svuotamento di tutte le tabelle. L’eliminazione viene confermata digitando il nome dell’app e non può essere annullata.
- L’assistente AI può elencare, cercare e leggere questi file per conto di un utente. Si applica lo stesso controllo
DATABACKEND/READ, quindi non mostra mai un file che l’utente non potrebbe aprire da solo. Cerca nei percorsi dei file, non nel loro contenuto, e legge file di testo, immagini e PDF — archivi e altri formati binari invece no.
Come per le tabelle, questi sono i dati dell’utente: può ispezionarli, limitarli ed eliminarli senza coinvolgerti.
Accesso S3 Diretto
Per tutto ciò che va oltre l’SDK — un analista con DuckDB, un backup notturno con rclone, una pipeline BI — un utente del progetto può emettere credenziali S3 di sola lettura per app dalle impostazioni di archiviazione dell’app.
Ogni credenziale è limitata all’area di archiviazione di quella singola app: una credenziale emessa per l’app A non funziona per i file dell’app B, strutturalmente — la policy di archiviazione di B semplicemente non la nomina mai. Un progetto può possedere diverse credenziali per app (una per consumatore: un job CI, uno script di backup, un laptop), e revocarne una lascia le altre funzionanti. Questa è anche la storia della rotazione: se una chiave trapela, emetti una seconda credenziale, sposta il consumatore su di essa, revoca la prima — nessun altro consumatore viene disturbato.
Il segreto viene mostrato una sola volta, alla creazione. L’emissione richiede accesso in lettura al data backend di quella app — lo stesso permesso che consente di leggere i file in primo luogo, per cui la credenziale non può mai ampliare la portata di nessuno.
# 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');Le impostazioni di archiviazione mostrano l’endpoint e il nome esatto del bucket a cui puntare un client. Nota che un elenco dei bucket di primo livello (aws s3 ls senza argomenti) non restituisce nulla per scelta progettuale — la credenziale non possiede alcun bucket; le viene concesso l’accesso a uno. Indirizza il bucket direttamente, come sopra.
Appliance On-Premises
L’archiviazione dei file funziona in modo identico su un’appliance on-premises, con tre differenze che discendono dal design dell’appliance:
- I file vengono serviti dalla stessa origine sotto l’indirizzo proprio dell’appliance (
/files/...) — nessun nome DNS aggiuntivo, nessun certificato aggiuntivo, e funziona nelle installazioni in solo HTTP. Gli URL permanenti e<img src>si comportano esattamente come nel cloud. - Il backend di archiviazione dell’appliance trasmette in streaming i download dei file attraverso la piattaforma invece di reindirizzare a un host di archiviazione separato, quindi il servizio di archiviazione non viene mai esposto come una seconda origine.
- Le credenziali S3 dirette non sono disponibili sulle appliance — l’archiviazione a oggetti integrata non è in grado di esprimere concessioni di accesso per singola credenziale. La sezione semplicemente non compare lì nelle impostazioni di archiviazione. Tutto il resto, incluso il comportamento dei file di grandi dimensioni tramite l’SDK e la conservazione dichiarata, funziona allo stesso modo.
Un’appliance air-gapped serve tutto il traffico dei file localmente: i logo delle app, le immagini delle dashboard e i download dei file non necessitano di alcuna connettività internet.
Limiti a Colpo d’Occhio
| Limite | Valore | Da dove proviene |
|---|---|---|
| Trasferimento inline (singola chiamata) | 6 MiB | Comunicato a runtime; può essere alzato lato server |
| Oggetto singolo | 100 MiB predefinito, 5 GiB massimo | maxObjectBytes per namespace |
| Upload singolo | 5 GiB | Tetto massimo del PUT singolo dell’archiviazione a oggetti; il multipart non è ancora disponibile |
| Budget di archiviazione | 1 GiB dev / 10 GiB prod per impostazione predefinita | Suggerito dal template, deciso dall’utente |
| Lunghezza e caratteri della chiave | UTF-8, percorsi separati da / | ../, i caratteri di controllo e i prefissi riservati vengono rifiutati |