Le API di Openbeehive
Il server espone un'API Connect-RPC definita in
Protocol Buffers sotto
proto/openbeehive/v1.
Ogni RPC è raggiungibile tramite HTTP/JSON semplice, gRPC e gRPC-Web dallo stesso
URL. L'app stessa usa solo SyncService; gli altri servizi esistono per
script, sensori e integrazioni.
URL di base e forma del percorso
L'API è servita dallo stesso processo dell'app, sulla stessa origine:
POST <origin>/openbeehive.v1.<Service>/<Method>
Ad esempio https://app.openbeehive.org/openbeehive.v1.ApiaryService/ListApiaries
sul servizio in hosting, oppure la tua origine se fai self-hosting.
Servizi serviti
Sul server sono registrati dieci servizi (server/cmd/server/main.go):
| Servizio | RPC |
|---|---|
ApiaryService | CreateApiary, GetApiary, ListApiaries, UpdateApiary, DeleteApiary |
HiveService | CreateHive, GetHive, ListHives, UpdateHive, DeleteHive, RelocateHive |
QueenService | CreateQueen, ListQueens, UpdateQueen, DeleteQueen |
InspectionService | CreateInspection, ListInspections, DeleteInspection, AddInspectionPhoto, RemoveInspectionPhoto |
TaskService | CreateTask, ListTasks, SetTaskDone, DeleteTask |
TreatmentService | CreateTreatment, ListTreatments, DeleteTreatment |
HarvestService | CreateHarvest, ListHarvests, DeleteHarvest |
EventService | ListEvents |
StatsService | GetDashboard, GetHoneyStats |
SyncService | Pull, Push, Subscribe (stream lato server) |
Ogni RPC è limitato al tenant attivo del chiamante. Gli id di un altro tenant
si comportano come id sconosciuti (not_found).
Le scritture passano dalla sincronizzazione
Ogni RPC di scrittura (Create*, Update*, Delete*, RelocateHive,
SetTaskDone, AddInspectionPhoto, RemoveInspectionPhoto) viene applicato
tramite la stessa unione per campo e aggiunto allo stesso registro delle
modifiche di SyncService.Push. Una riga scritta tramite l'API raggiunge ogni
dispositivo alla sua sincronizzazione successiva, e una modifica successiva da
un dispositivo si unisce ad essa campo per campo, esattamente come le modifiche
di due dispositivi si uniscono tra loro. Le cancellazioni sono logiche: la riga
riceve una tombstone (deleted = 1) e scompare dagli elenchi e dall'app.
Le scritture lato server rispecchiano i flussi dell'app stessa
(server/internal/service/writer.go è un port di
app/src/lib/local/history.ts), quindi la storia che un dispositivo mostra è
la stessa sia che un record sia stato inserito nell'app sia tramite l'API:
CreateHivescrive l'arnia, apre un intervallo di collocazione e registra un eventoCREATED. Le nuove arnie partono comeHIVE_STATUS_ACTIVE.RelocateHivechiude la collocazione aperta, ne apre una nuova nell'apiario di destinazione e registra un eventoMOVED.UpdateHivenon sposta mai un'arnia.CreateQueentermina il regno della regina attiva dell'arnia (active = false,replacedAtimpostato all'introducedAtdella nuova regina), registra gli eventiQUEEN_REPLACEDeQUEEN_INTRODUCED, e ricava il colore di marcatura dayearquando non ne invii uno.CreateInspectioneCreateTreatmentfissano l'apiario e la regina regnante alladatedel record (un record retrodatato viene risolto rispetto alla storia delle collocazioni e delle regine) e registrano un eventoINSPECTIONoTREATMENT.CreateHarvestfissa allo stesso modo l'apiario e la regina regnante alladate, scrive la riga del raccolto e registra un eventoHARVESTche portaamountKge il titolo<kg> kg <variety>(Honeyquandovarietyè vuoto).GetDashboardeGetHoneyStatssommano quegli eventi, quindi un raccolto inviato tramite l'API conta subito nelle cifre sul miele.amountKgdeve essere maggiore di0.- Un'attività con
hiveIdoapiaryIdsi sincronizza sotto quell'apiario ed è condivisa con tutti coloro che lo vedono; un'attività senza nessuno dei due vive nello scope personale del chiamante e raggiunge solo i dispositivi di quell'utente.
Le letture (Get*, List*, ListEvents, GetDashboard, GetHoneyStats)
interrogano direttamente le tabelle del server. Mostrano subito le scritture
dell'API e quelle dei dispositivi non appena il dispositivo le ha inviate.
Non ancora nell'API
- Nessun RPC di modifica dei raccolti.
HarvestServiceha soloCreateHarvest,ListHarvestseDeleteHarvest. Per correggere un raccolto, eliminalo e crealo di nuovo, oppure modificalo nell'app. - Nessun RPC per le collocazioni. Le collocazioni vengono scritte da
CreateHiveeRelocateHivee compaiono solo nel feed di sincronizzazione. EventServiceè in sola lettura. Gli eventi vengono scritti dai flussi qui sopra e dall'app.
Vedi il protocollo di sincronizzazione per il formato delle modifiche e il modello dei dati per le colonne dietro ogni messaggio.
Endpoint non RPC
Accanto ai servizi RPC ci sono alcuni endpoint HTTP semplici:
| Percorso | Scopo |
|---|---|
GET /healthz | Restituisce ok |
GET /files/<key> | File archiviati, solo con il backend blob su filesystem (BEEHIVE_BLOB_BACKEND=fs) |
POST /auth/signin, POST /auth/signup, GET /auth/verify | Account con e-mail/password (BEEHIVE_PASSWORD_AUTH=true) |
GET /auth/login, GET /auth/callback | OIDC (BEEHIVE_OIDC_PROVIDERS impostata) |
/auth/webauthn/login/*, /auth/webauthn/enroll/*, /auth/webauthn/credentials* | Passkey (BEEHIVE_WEBAUTHN_ENABLED=true) |
/auth/logout, /auth/me, /auth/instance, /auth/switch, /auth/accept-invite | Helper per sessione e tenant, presenti quando è abilitato un qualsiasi metodo di login |
GET /auth/api-keys, POST /auth/api-keys, POST /auth/api-keys/delete | Gestione delle chiavi API (elenco, creazione, rimozione), presente quando è abilitato un qualsiasi metodo di login; solo con sessione, una chiave non può chiamarli |
/tenants/create, /tenants/invite, /tenants/invites, /tenants/invite/revoke, /tenants/delete | Amministrazione dei tenant, presente quando è abilitato un qualsiasi metodo di login |
POST /auth/demo-login | Solo con BEEHIVE_DEMO=true |
Questi endpoint sono usati dalle schermate di login e impostazioni dell'app. Non fanno parte del contratto proto.
Autenticazione
Il modo in cui una richiesta viene autenticata dipende dal fatto che l'istanza abbia o meno un metodo di login:
- Nessun login configurato (self-hosted,
BEEHIVE_PASSWORD_AUTH=false,BEEHIVE_OIDC_PROVIDERSvuota,BEEHIVE_WEBAUTHN_ENABLED=false): ogni richiesta viene eseguita come l'utente locale fisso. Non inviare credenziali. Su un'istanza del genere non esistono chiavi API; non ne servono. - Login configurato: ogni RPC richiede una chiave API o un token di
sessione in
Authorization: Bearer <token>(per i token di sessione è accettato anche il cookieobh_session). Una richiesta senza un token valido riceve il codice Connectunauthenticated(HTTP 401).
Chiavi API
Una chiave API è la credenziale consigliata per script, sensori e integrazioni. Le chiavi si gestiscono nell'app in Impostazioni → Chiavi API (vedi Account e tenant):
- Accedi, passa al tenant in cui lo script deve scrivere e apri Impostazioni → Chiavi API.
- Digita un nome (per esempio
hive scale), scegli i permessi (Lettura e scrittura o Sola lettura) e una scadenza (Non scade, 30 giorni, 90 giorni o 1 anno), poi tocca Crea chiave. La chiave inizia conobhk_e viene mostrata una sola volta; copiala subito. Il server memorizza solo il suo hash SHA-256 e i primi 12 caratteri per la visualizzazione. - Inviala a ogni RPC:
Authorization: Bearer obhk_...
Cosa una chiave può e non può fare:
- Agisce come il suo proprietario, con il ruolo del proprietario, all'interno del tenant che era attivo quando la chiave è stata creata. Non può cambiare tenant; crea una chiave per ogni tenant se uno script ne ha bisogno di più di uno.
- Funziona solo sugli RPC. Gli endpoint
/auth/*e/tenants/*, compresa la gestione delle chiavi stessa, richiedono una sessione vera: una chiave non può elencare, creare o rimuovere chiavi. - Ha uno di due scope, fissato alla creazione. Una chiave
write(il valore predefinito) può chiamare ogni RPC che il suo proprietario può chiamare. Una chiavereadpuò chiamare solo gli RPC il cui nome inizia conGet,List,PulloSubscribe; ogni altro RPC restituisce il codice Connectpermission_denied(HTTP 403) con il messaggiothis API key is read-only. - Scade solo se imposti una scadenza:
expires_in_daysda0(mai, il valore predefinito) a3650. Superatoexpires_atogni RPC restituisceunauthenticated(HTTP 401) con il messaggioAPI key expired. Le chiavi scadute restano nell'elenco finché non le rimuovi. - Smette di funzionare nel momento in cui viene rimossa nelle Impostazioni, e muore con l'appartenenza: quando il proprietario lascia il tenant o ne viene rimosso, la chiave viene rifiutata.
- Ogni utilizzo aggiorna
last_used_at, mostrato come Ultimo utilizzo nelle Impostazioni. - L'account demo non può creare chiavi.
Gli stessi endpoint usati dall'app sono aperti a uno script autenticato con sessione:
GET /auth/api-keysrestituisce{"keys": [{id, name, prefix, scope, created_at, expires_at, last_used_at, mine}]}, solo le tue chiavi nel tenant attivo.scopeè"write"o"read";expires_atelast_used_atsono timestamp RFC 3339 oppurenull;minequi è sempretrue.GET /auth/api-keys?tenant=1restituisce ogni chiave del tenant attivo, con ogni riga che porta in piùuser_id,user_emaile un flagmineche ètruesulle tue chiavi. Possono chiamarlo solo il proprietario del tenant (il ruolo che l'app chiama Admin) e l'admin dell'istanza; tutti gli altri ricevono HTTP 403.POST /auth/api-keyscon{"name": "...", "scope": "write", "expires_in_days": 0}restituisce{id, name, prefix, scope, created_at, expires_at, token}. Se omessi,scopevale"write"eexpires_in_daysvale0(mai). Qualsiasi altro scope, o una durata inferiore a0o superiore a3650, risponde HTTP 400.POST /auth/api-keys/deletecon{"id": "..."}risponde204. Un membro può rimuovere solo le proprie chiavi; l'id di qualcun altro risponde 404. Il proprietario del tenant e l'admin dell'istanza possono rimuovere qualsiasi chiave del tenant attivo. L'override non attraversa mai i tenant: il proprietario di un altro tenant riceve 404 per le chiavi che non sono sue.
Accesso con sessione
L'alternativa è il token di sessione dell'app, un valore firmato con HMAC che
il server emette al login. È accettato sugli RPC e sugli endpoint /auth/* e
/tenants/*. Con l'autenticazione a password abilitata uno script ne ottiene
uno con:
curl -s -X POST https://bees.example.com/auth/signin \
-H "Content-Type: application/json" \
-d '{"email":"you@example.com","password":"..."}'
La risposta JSON contiene token; lo stesso valore viene impostato anche come
cookie obh_session. Il token scade dopo BEEHIVE_SESSION_TTL.
Il token porta con sé il tenant attivo dell'account. GET /auth/me elenca i
tenant a cui appartieni (tenants) e quello attivo (active_org);
POST /auth/switch con {"org_id": "..."} restituisce un nuovo token per
un altro tenant.
Con BEEHIVE_DEMO=true le sessioni demo sono in sola lettura: l'account demo
può chiamare solo gli RPC il cui nome inizia con Get, List, Pull o
Subscribe. Ogni altro RPC restituisce permission_denied.
Vedi Autenticazione per come configurare i metodi di login.
Pagine sui protocolli
- REST / HTTP + JSON: esempi con curl, la forma del JSON e il contratto di paginazione.
- gRPC: client generati e la chiamata in streaming
Subscribe. - Tracker automatici: inviare letture di bilance e sensori climatici da uno script o da un dispositivo.