Documentatie
Flows en automatisering
Een flow is een tekening die draait. Je zet knopen op een canvas, trekt er draden tussen, en het platform loopt die tekening af zodra het moment aanbreekt dat je gekozen hebt. Deze pagina zegt wat een flow kan, wat elke knoop betekent, en waar precies zijn grenzen liggen.
Wat een flow is
Een flow is een gerichte graaf: knopen met draden ertussen, afgelopen vanaf één beginpunt. Het is bewust geen script met een lus. Vier regels leggen de vorm vast, en de editor weigert een tekening te publiceren die er een van breekt, met de knoop erbij die niet klopt.
- Precies één trigger. De triggerknoop is de enige ingang, en hij bepaalt welke variabelen er in de rest van de tekening bestaan.
- Geen cycli. Een draad die terugleidt naar een knoop waar hij vandaan kwam wordt geweigerd, want een flow kan niet wachten: een lus zou eenvoudigweg nooit eindigen.
- Elke knoop moet vanaf de trigger bereikbaar zijn. Een knoop die los hangt is een fout die je op een schermafbeelding niet ziet, dus het is een fout bij het publiceren.
- Takken worden na elkaar afgelopen, nooit naast elkaar. Twee takken tegelijk zouden tegelijk in state schrijven, en de uitkomst zou van de timing afhangen.
Het canvas
De editor is een canvas en er is geen tweede. Een nieuwe flow opent er meteen op, en een flow die een rechte lijst met stappen was opent als een rechte keten van knopen.
- Het palet bevat de twaalf knoopsoorten. Klik er een aan om hem op het canvas te zetten, of sleep hem naar de plek die je wilt.
- Versleep een knoop aan zijn kop om hem te verplaatsen. Al het andere op de knoop is een bedieningselement en begint dus geen sleepbeweging.
- Een draad loopt van een uitgangsstip naar een ingangsstip. Sleep van de een naar de ander, of klik eerst de uitgang en dan de ingang aan. Allebei doen hetzelfde, en het tweede is de toetsenbordweg.
- Laat een draad in het niets los en er gebeurt niets. Verwijderen doe je door hem te selecteren en op Delete te drukken, of met het kruisje dat erop staat.
- Zoom tussen 50 en 200 procent, gebruik Passend om de hele tekening te zien, en versleep de achtergrond om te pannen. Er is geen minimap.
De knopen
Twaalf soorten, en die lijst is met opzet kort. Elke soort is een paneel, negen vertalingen, een tak in de uitvoerder en een rij in de waarheidstabel, dus een soort die zijn gewicht draagt is meer waard dan een soort die dat bijna doet.
| Knoop | Uitgangen | Wat hij doet, en waar hij ophoudt |
|---|---|---|
trigger | out | De enige ingang. Zijn gebeurtenis bepaalt welke variabelen bestaan. Een handmatige trigger draagt ook de JSON die je injecteert: hoogstens 2048 tekens, 40 sleutels en 512 tekens per waarde. |
script | ok · error | Rendert een scriptsjabloon tegen een doel. Hij verlaat de knoop via ok, of via error als het renderen mislukt. Vink doorgaan bij een fout aan en een mislukking loopt tóch door. |
switch | rule-0 … · otherwise | Kijkt naar één waarde en neemt de eerste regel die past; al het andere gaat via otherwise. Hoogstens twaalf regels, en een vergeleken waarde van hoogstens 512 tekens. |
if | true · false | Eén voorwaarde, twee uitgangen. Dezelfde operatoren en dezelfde evaluatie als een switch-regel, getekend als de ja-of-nee die het werkelijk is. |
set | out | Schrijft in state, waar een latere knoop of een script het weer uitleest. Hoogstens twaalf toekenningen per knoop en 4096 tekens per waarde. De namen kies je zelf; http is gereserveerd. |
http | ok · error | Roept een URL aan en zet het antwoord in state.http. GET of POST, hoogstens acht headers, een timeout tussen 0,5 en 10 seconden, en een antwoordgrens tussen 256 bytes en 256 KiB, standaard 64 KiB. Hoogstens drie redirects, elk opnieuw gezeefd. |
notify | out | Schrijft een melding in het paneel van dit account, op niveau info of waarschuwing. Kop hoogstens 120 tekens, tekst hoogstens 1000. Vink Ook per e-mail versturen aan en hij gaat er ook per e-mail uit, naar iedereen met toegang tot Scripts die Flowmeldingen niet heeft uitgezet onder E-mailmeldingen. Een proefrun verstuurt nooit e-mail. |
action | ok · error | Start, stopt, sluit af, schort op of hervat de server waar deze run over gaat, en wacht tot 300 seconden op de afronding van het commando. Nul betekent niet wachten: aanvaard is niet hetzelfde als draaiend. |
delay | out | Pauzeert de run. Tot 30 seconden binnen het verzoek dat hem startte; langer en de run wordt weggelegd en later opgepakt, tot hoogstens 30 dagen. |
approval | approved · rejected | Legt de run stil tot een mens goedkeurt of afwijst, met een deadline en een gekozen uitkomst als niemand antwoordt. |
foreach | loop · done | Loopt de lusuitgang één keer per element van een lijst af, gelezen als JSON, één per regel, of gescheiden door komma's. |
repeat | loop · done | Loopt de lusuitgang af zolang een voorwaarde waar is, en leest die voorwaarde vóór elke ronde opnieuw. |
break | — | Verlaat de lus waar hij in staat; de run gaat verder bij de klaar-uitgang van die lus. |
subflow | ok · error | Voert een andere flow van dit account uit en wacht op de uitkomst. Het kind krijgt zijn invoer als input.*. |
log | out | Schrijft één regel in de run, zoals de debug-knoop dat in Node-RED doet. Het veld bevat 1000 tekens; wat in de run belandt zijn de eerste 200. |
note | — | Tekst op het canvas. Hij heeft geen poorten en wordt nooit afgelopen, dus hij komt ook nooit in een run voor. |
end | — | Beëindigt deze tak en zegt hoe de run afliep: geslaagd, mislukt, of overgeslagen voor een server waar deze flow niet voor bedoeld was. Er hoort een boodschap van hoogstens 280 tekens bij. |
Triggers
Vijf momenten, en welk moment je kiest is niet alleen wanneer de flow draait. Het is ook welke variabelen bestaan, welke de kiezer je aanbiedt, en welke het platform weigert op te slaan. Een pad dat op dat moment niet kán bestaan is een fout tijdens het opslaan, geen lege tekst midden in een run.
| Trigger | Wanneer hij draait | Wat er is, en wat niet |
|---|---|---|
server.provisioned | Eén keer, meteen nadat een bestelling de machine heeft opgeleverd. | Alles. Dit is het enige moment waarop elke groep bestaat. |
server.reinstalled | Nadat een herinstallatie van een bestaande machine geslaagd is. | Dezelfde machine, een ander image. Het adres is ongewijzigd en de aanmaakdatum is nog steeds de oorspronkelijke. Vandaag vuurt hij alleen voor containers; een herinstallatie van een virtuele machine wordt afgerond door een onderdeel dat niet kan renderen. |
server.deleted | Zodra een verwijdering aanvaard is, terwijl de rij nog leesbaar is. | Alles behalve het image. Er is niets meer om een image uit te lezen, en het platform verzint er geen, dus os ontbreekt gewoon. |
manual | Nooit uit zichzelf. Je start hem in de editor met Inject. | Een bestaande machine die je aanwijst, plus je eigen JSON als input. Ook hier is er geen image: wat er op een machine staat kan het platform achteraf niet opzoeken. |
webhook | Zodra een vreemde naar een eigen URL post. | Helemaal geen machine, dus geen plan, geen adres, geen koppeling en geen agent. Wat overblijft is het account, de flow, de geposte body als input, en het verzoek zelf als webhook. |
Variabelen
Elke waarde die een flow kan lezen komt uit een van deze groepen. De kiezer in de editor toont de groepen die bij je trigger bestaan en verder niets. Een onbekend pad lezen is een fout en nooit een lege waarde: een verkeerde aanname hoort luid te zijn.
| Groep | Bestaat bij | Wat erin zit |
|---|---|---|
org.* | Elke trigger | Het account: id, naam en slug. |
user.* | De vier servertriggers | De persoon voor wie de run handelt: id, naam en e-mailadres. |
flow.* | Elke trigger | Deze flow en deze run: naam, run-id, stapnummer en aantal, trigger, en wanneer hij begon. |
server.* | De vier servertriggers | De machine: naam, hostnaam, soort, status, extern id, node, cores, geheugen, schijf en aanmaakdatum. |
server.compute.* | De vier servertriggers | Wat voor gast het is en wat die soort werkelijk kan: soort, template, unprivileged, opslag, mountpoints, capabilities en unsupported. Alle zeven zijn optioneel, en ontbreken betekent niet vastgesteld en niet nee. |
plan.* | De vier servertriggers | Het plan achter de bestelling: naam, cores, geheugen, schijf en bridge. |
os.* | Aangemaakt en herinstalleerd | Het image: naam, slug en categorie. Er is geen familie en geen versie, want de catalogus heeft die kolommen niet en ze raden kiest de verkeerde pakketbeheerder. |
ip.* | De vier servertriggers | Het adres dat is uitgegeven, en of het versie 4 of 6 is. |
network.* | De vier servertriggers | Alles rond dat adres: cidr, gateway, netmasker, netwerk, broadcast en de resolvers. |
prefix.* | De vier servertriggers | De adrespool waar het uit komt, met zijn bereik en zijn ouder. |
integration.* | De vier servertriggers | De koppeling die de machine draagt: naam, soort, node, opslag, bridge en het vmid-bereik. |
agent.* | De vier servertriggers | De agent die het werk deed: naam, status, versie, hostnaam, besturingssysteem, kernel en cores. |
input.* | Handmatig en webhook | Wat je injecteerde, of wat de webhook postte. De sleutels zijn van jou, en de kiezer volgt ze. |
webhook.* | Alleen webhook | Het verzoek zelf: welk endpoint, wanneer het binnenkwam, en van welk adres. |
state.* | Na een set-knoop | Wat een set-knoop in deze flow schreef. De namen zijn van jou, en ze bestaan alleen onder de knoop die ze schrijft. |
state.http.<node>.* | Na een http-knoop | Per http-knoop: status, ok, body, json, error en url. Alle zes bestaan altijd, op het goede pad en op het slechte. |
device.* | Nog nergens | Ontworpen voor netwerkapparatuur. Er is geen provider die hem kan vullen, dus geen enkele flow kan hem vandaag gebruiken. |
Expressies
Een set-knoop schrijft in state. Zijn waarde is letterlijke tekst, tenzij je het vinkje voor expressies zet; dan wordt hij gelezen als een sjabloon, met de variabelen van deze run ingevuld.
- Dat vinkje is een bewuste keuze. Een waarde die toevallig twee accolades bevat, een stuk Ansible of een fragment Helm, blijft betekenen wat er stond. Er verandert niets van betekenis door een wijziging waar niemand om vroeg.
- De taal is alleen uitvoer. Twee accolades en een pad, en verder niets: geen assign, geen capture, geen lus, geen include. Wat op het canvas staat is wat eruit komt, met de waarden ingevuld.
- Er zijn 46 filters: tekst, getallen, datums, de paar lijstfilters die tekst opleveren, en de eigen quote, ini en ipoctet. De kiezer in de editor somt ze op. Twee families ontbreken met opzet, die welke dobbelen en die welke een object teruggeven waar je een woord wilde.
- Geheimen zijn hier niet beschikbaar, en snippets evenmin. Wat een set-knoop schrijft belandt in het verslag van de run, en daar heeft een geheim niets te zoeken.
Goedkeuringen en lussen
Een flow kan stoppen en op een mens wachten, en hij kan dezelfde knopen meer dan eens aflopen. Allebei kon niet zolang een run niet stilgezet en later gewekt kon worden; die wachtrij is er nu.
- Een goedkeuringsknoop legt de run stil, schrijft een verzoek en meldt het aan het account. Beantwoorden vraagt een apart recht, zodat wie de flow schreef niet vanzelf degene is die hem aftekent.
- Elk verzoek draagt een deadline en wat er zonder antwoord gebeurt: doorgaan als goedgekeurd, doorgaan als afgewezen, of de run beëindigen als niet van toepassing. Er is geen vierde keuze, want een run die eeuwig wacht is een run waarvan niemand ooit nog hoort.
- Voor elk loopt zijn lusuitgang één keer per element van een lijst af. In de lus bevat state.loop het huidige element, zijn plaats en het totaal.
- Er mag niets van buiten een lus aan de knopen erbinnen hangen. Die knopen draaien elke ronde opnieuw, en een melding die aan allebei hangt zou per ronde één bericht schrijven.
- Een goedkeuringsstap kan ook post versturen. Die gaat alleen naar de leden die over goedkeuringen mogen beslissen, niet naar iedereen die flows kan zien, en de paneelmelding wordt hoe dan ook geschreven.
Wachten, en flows die flows aanroepen
Een run die stilstaat is geen run die mislukt is. Drie dingen kunnen er een wegleggen, en alle drie gebruiken ze dezelfde wachtrij: een goedkeuring, een lange pauze, en een subflow waarvan het kind zelf staat te wachten.
- Een pauze van een halve minuut of korter wacht gewoon binnen het verzoek dat hem startte, en dat is wat je wilt als je een agent even de tijd geeft. Alles wat langer duurt legt de run weg, zodat de bestelling die hem startte meteen klaar is en de rest van de flow later volgt.
- Een subflow voert een andere flow van dit account uit als één stap. Zijn uitkomst bepaalt de uitgang: een kind dat slaagde of niets te doen vond gaat over OK verder, een kind dat mislukte over Fout. De stand, de run en de flownaam staan in state.subflow.
- Een flow mag zichzelf nooit bereiken, rechtstreeks niet en via een andere flow ook niet. Dat wordt bij het deployen gecontroleerd, en de melding noemt de keten die gevonden is.
- Flows mogen elkaar hoogstens drie niveaus diep aanroepen. Omdat flows los van elkaar bewerkt worden, wordt die grens twee keer gecontroleerd: één keer bij het deployen en één keer terwijl de run werkelijk loopt.
Webhooks
Een webhook-endpoint geeft een systeem van buiten een eigen URL. Het post JSON, de flow achter de URL draait, en het antwoord is een kale statuscode. Endpoints staan onder Scripts, op het tabblad Webhooks, en zijn eigen objecten: hetzelfde endpoint kan naar een andere flow gaan zonder dat de afzender er iets van merkt.
- Maak het endpoint aan en geef het een naam die je later herkent. De URL wordt voor jou gemaakt en is niet te kiezen: 32 willekeurige tekens, oftewel 160 bits, dus raden is geen strategie.
- Kies hoe de afzender zich bewijst, en kopieer het geheim. Het wordt één keer getoond en daarna nooit meer; ben je het kwijt, dan regenereer je, en dan krijg je ook een nieuwe URL.
- Wijs het endpoint naar een flow met een webhook als trigger. Het endpoint mag eerder bestaan dan de flow, en tot dan wordt een verzoek aanvaard en beantwoord met een 202 die niets start. Dat is de volgorde waarin mensen dit werkelijk bouwen, dus het geldt niet als een fout.
| Schema | Wat het doet |
|---|---|
none | Helemaal geen bewijs: wie de URL kent kan de flow starten. Te verdedigen met een adreslijst ernaast, en moeilijk te verdedigen zonder. |
hmac_sha256 | De afzender ondertekent de rauwe body samen met een tijdstempel. Dit is de keuze: hij bindt het verzoek aan zijn eigen body, dus een onderschept verzoek is niet te bewerken en door te sturen. |
bearer | De afzender zet het gedeelde token in de Authorization-header. Eenvoudig, en het zegt niets over de body die eraan vastzat. |
De handtekening is het woord sha256, een isgelijkteken, en de HMAC-SHA256 over precies de bytes van het tijdstempel, een punt, en de body, in kleine letters hexadecimaal. Hij gaat in X-Provibr-Signature, en het tijdstempel zelf gaat in X-Provibr-Timestamp als hele seconden sinds het epoch.
Een verzoek waarvan het tijdstempel meer dan 300 seconden oud is, of in de toekomst ligt, wordt geweigerd met een 401. Dat venster is een echte regel en geen versiering, want het tijdstempel zit ín het ondertekende materiaal: een onderschept verzoek is morgen niet opnieuw te versturen met een vers tijdstempel eroverheen geplakt.
Een verzoek ondertekenen is één regel openssl. Dit voorbeeld post een klein object; de shell-tekst blijft met opzet Engels, zodat hij overeenkomt met wat je terminal toont.
# Sign the raw body together with the timestamp, then post it.
SECRET="$PROVIBR_WEBHOOK_SECRET"
BODY='{"reason":"maintenance","attempt":1}'
TS=$(date +%s)
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -r | cut -d' ' -f1)
curl -sS -X POST 'https://app.provibr.com/api/hooks/flows/<your-slug>' \
-H 'Content-Type: application/json' \
-H "X-Provibr-Timestamp: $TS" \
-H "X-Provibr-Signature: sha256=$SIG" \
--data "$BODY"| Grens | Details |
|---|---|
body | Standaard 64 KiB, instelbaar tussen 1 KiB en 256 KiB. Een groter verzoek wordt afgekapt terwijl het nog binnenkomt, niet eerst volledig gelezen en dan geweigerd. |
rate | Standaard 60 verzoeken per minuut, instelbaar tussen 1 en 600. Hij telt per endpoint en niet per afzender, want een echte bron heeft één adres en een aanvaller heeft er duizend. |
payload | Een JSON-object van één niveau diep. Elke sleutel wordt input gevolgd door die sleutel; een genest object wordt geweigerd en niet platgeslagen. |
allowlist | Hoogstens 20 adressen of bereiken, in v4 of v6. Leeg betekent helemaal geen beperking. |
endpoints | Hoogstens 50 endpoints per account. |
| Code | Betekenis |
|---|---|
202 | Aanvaard. Er is een run gestart, of er was niets te starten: nog geen flow gekoppeld, de flow staat uit, of de run zelf viel om. In alle vier de gevallen deed de afzender niets fout, en een bonnetje zegt welke van de vier het was. |
401 | De handtekening of het token ontbreekt of klopt niet, of de handtekening klopt en zijn tijdstempel valt buiten het venster. |
403 | Het endpoint staat uit, of het bronadres staat niet op de adreslijst. |
404 | Er is geen endpoint met deze slug. |
410 | Het endpoint is ingetrokken. De slug blijft voorgoed bezet, en dit antwoord zegt een eerlijke bron dat hij een nieuwe URL nodig heeft, wat een 404 niet zou doen. |
413 | De body is groter dan dit endpoint aanneemt. |
422 | De body is geen JSON-object van één niveau. |
429 | Over de doorvoerrem van dit endpoint heen. |
Elk verzoek laat een bonnetje achter, aanvaard of niet: de uitkomst, de status die terugging, het bronadres voluit, en hoeveel bytes er werkelijk gelezen zijn. De body wordt nooit bewaard. Bonnetjes verdwijnen na 90 dagen, eerder dan de runs zelf, want een bronadres is een persoonsgegeven van iemand die onze klant niet is.
Revisies en runs
Twee vragen die op elkaar lijken hebben hier twee verschillende antwoorden. Wie wat wanneer veranderde is een revisie. Wat deze ene run werkelijk aflegde is een kopie die op de run zelf staat. Een momentopname kan de eerste vraag niet beantwoorden en een revisie de tweede niet, dus ze bestaan allebei.
- Elke publicatie schrijft een revisie, ook een publicatie die niets veranderde. Het nummer loopt per flow op en wordt nooit hergebruikt.
- Het verschil tussen twee revisies is een leesbare lijst en geen rauwe JSON: een knoop erbij, weg of gewijzigd met de velden erbij, een draad erbij of weg. Waar een knoop op het canvas staat is geen wijziging.
- Herstellen draait niets terug. Het schrijft de oude tekening als een nieuwe revisie bovenop de geschiedenis, met een aantekening waar hij vandaan komt.
- Elke run bewaart de tekening die hij afliep. Een flow die daarna wijzigt herschrijft nooit wat een oudere run deed.
- Een run bevat een regel per knoop die bezocht is, met de uitkomst en de uitgang waarlangs hij vertrok, en een regel per knoop die niet bezocht is met de reden. Niet bezocht is een feit dat het opschrijven waard is, geen afwezigheid.
- De runlijst filtert op trigger, status en revisie, en exporteert naar CSV. De export stopt bij 5000 regels en zegt dat ín het bestand in plaats van stilletjes op te houden.
Scriptsjablonen houden hiernaast een eigen geschiedenis bij: elke bewaarde versie van de inhoud, en een regel-voor-regelverschil tussen twee ervan.