Infrastruktur · Medien

keepix.cloud — ein Medienhost, der fertige Adressen zurückgibt

Ein Medienservice für Apps, Verkaufsstellen und Marktplätze: eine signierte Anfrage hinein — benannte WebP-Varianten und eine HLS-Treppe heraus, bereitgestellt von der eigenen Subdomain des Kunden mit einem ewigen Cache. Multi-Tenant, verbrauchsabhängige Abrechnung bis zu Hundertsteln eines Cents, EXIF bei der Aufnahme entfernt, und ein Endpoint zum Löschen nach Person, der mit einer Nummer antwortet. Wir haben das Ganze entworfen und gebaut, von der Aufnahme-Pipeline bis zum Dashboard.

Kategorie
Infrastruktur · Medien
Geschätzte Kosten
ab 24.000 €
Zeitachse
≈ 10–14 Wochen
keepix.cloud
keepix
Projekt-Homepage — keepix.cloud

01Projektübersicht

keepix.cloud ist ein Medienhost für Produkte, die die Dateien anderer Personen bereitstellen müssen: Katalogfotos in einem Geschäft, Bewertungsbilder in einem Marktplatz, Walkthrough-Videos in einer App. Sie senden das Original in einer signierten Anfrage, und die Antwort enthält bereits die fertigen Adressen – ein benanntes Set von WebP-Varianten für ein Bild, eine HLS-Treppe für ein Video. Die Dateien werden von der eigenen Subdomain des Kunden bereitgestellt, sodass der Leser seine Marke sieht und uns niemals sieht.

Die Prämisse ist, dass Medien kein Feature sind, das man einmal pro Projekt erstellt. Jedes Produkt, das es Menschen ermöglicht, etwas hochzuladen, endet damit, dass es dasselbe wiederholt: Größenänderung, Formatkonvertierung, ein Benennungsschema, eine Cache-Politik, das Entfernen von Metadaten, ein Pfad, um alles über diese Person zu löschen. Zum fünften Mal geschrieben, wird es zum fünften Mal schlecht geschrieben – und die Teile, die schiefgehen (ein geleakter GPS-Tag, ein SVG, das sich als Skript herausstellt), schlagen stillschweigend fehl, ohne einen Fehler und ohne eine Protokollzeile.

Also haben wir den Dienst um eine Idee herum aufgebaut: die Adresse wird vom Dienst deklariert und vom Kunden gespeichert, niemals auf der Seite des Kunden berechnet. Diese einzige Entscheidung beseitigt die häufigste Art, wie Medienintegrationen fehlschlagen – eine Seite, die perfekt gerendert wird, mit einem Loch, wo das Produktfoto sein sollte.

Wir haben das gesamte Produkt entworfen und gebaut: die Eingangs-Pipeline, das Variantenmodell, die Videoleiter, das Multi-Tenant-Modell mit eigenen Domains und Signaturschlüsseln, die verbrauchsabhängige Abrechnung, das Dashboard, den öffentlichen API-Vertrag und die Dokumentation. Unten ist, welches Problem es löst, wie es zusammengesetzt ist und welche technischen Entscheidungen wir auf die gleiche Weise wieder treffen würden.

02Kontext und das Problem

Das Speichern eines hochgeladenen Bildes ist einfach. Einen Medienpfad zu führen, der über Jahre hinweg unter Last, über mehrere Produkte und die Dateien anderer Personen hinweg korrekt bleibt, ist es nicht. Als wir die Aufgabe entpackten, fielen die Fallstricke schnell auf.

  • Willkürliche Breiten sind eine Falle.Eine URL wie ?w=473 sieht flexibel aus und ist in der Tat zwei Probleme auf einmal: ein Cache, der nie aufwärmt, weil jede Seite nach einer leicht unterschiedlichen Zahl fragt, und eine kostenlose Möglichkeit für jeden, den Prozessor zu beanspruchen, indem er tausend Breiten hintereinander anfordert.
  • Eine berechnete Adresse bricht stillschweigend.Die Regel "id → shard → path", die im Code des Clients verankert ist, überlebt genau bis zur ersten Layoutänderung auf der anderen Seite. Dann wird die Seite weiterhin gerendert und das Produkt hat einfach ein Loch, wo sein Foto war – kein Fehler, nichts im Protokoll.
  • Metadaten sind ein Leck, das niemand bemerkt.Die GPS-Koordinaten des Hauses von jemandem, die im EXIF eines Bewertungsfotos sitzen, erzeugen keinen Fehler, brechen keine Seite und erreichen keine Überwachung. Es ist nur ein Leck, wenn jemand hinschaut.
  • SVG ist kein Bild.Es ist ausführbare Markup. Akzeptiert als Bild und von der eigenen Domain des Clients bereitgestellt, läuft ein Skript darin im Namen dieser Domain.
  • Löschung muss nachweisbar sein.Eine GDPR-Löschanfrage, die beantwortet wird, indem Zeilen als gelöscht markiert werden, während die Bytes auf der Festplatte bleiben, ist eine Löschung, die nicht stattgefunden hat.
  • Video ist ein Set, kein Datei.Ein Upload muss zu einer abspielbaren Leiter, einem Posterrahmen und einem Fallback werden – und nichts davon kann innerhalb der Anfrage geschehen, die die Datei akzeptiert hat.
  • Abrechnung muss bei kleinen Zahlen ehrlich sein.Ein Gigabyte kann weniger als einen Cent kosten; das Runden auf ganze Cents frisst die gesamte abgerechnete Rechnung und macht die Rechnung zur Fiktion.

Es gab eine weitere Anforderung, die das gesamte Design prägte: der Dienst darf überhaupt nicht ins Internet gehen. Ein Medienhost, der ein Bild im Auftrag des Kunden von einer Drittanbieteradresse abruft, weiß, wer was angefordert hat, und gibt dieses Wissen an das verwendete CDN weiter. keepix kennt keine Drittanbieteradresse, und das ist eine Eigenschaft des Codes, kein Versprechen auf der Preisseite.

03Projektziele

Die Aufgabe verwandelte sich in eine Liste von Zielen, an denen wir in jeder Phase festhielten:

  • Eine Anfrage rein, fertige Adressen raus.Der Kunde sendet die Originale und erhält sofort die Adressen zurück, die gespeichert werden sollen – kein zweiter Anruf, kein Warten auf den Abschluss eines Auftrags für Bilder.
  • Eine benannte, endliche Menge von Varianten.Drei Breiten für Bilder und vier Formen für Videos, je nach Dateityp deklariert, damit der Cache warm ist und der Prozessor nicht zur Miete steht.
  • Die eigene Domain des Kunden. Dateien, die von img.theirdomain, mit dem Dienst, der in der Adressleiste unsichtbar ist.
  • Ein Cache, der für immer behalten werden kann. Die Adresse enthält einen Hash des Inhalts, sodass das Ersetzen einer Datei ihre Adresse ändert und den Leser sofort erreicht.
  • Sicherheit, die in den Pfad eingebaut ist. Typ, der durch Parsing bestimmt wird, ein Virenscan, der ablehnt, anstatt Dinge durchzulassen, erneutes Kodieren bei jedem Bild, Metadaten entfernt, SVG an beiden Enden abgelehnt.
  • Nachweisbare Löschung. Eine Anfrage von der eigenen owner_ref, und eine Antwort, die angibt, wie viele Objekte und Dateien gegangen sind.
  • Multi-Tenancy mit echtem Isolation.Eigene Domains, eigene Arten und Varianten, eigene Obergrenzen, zwei Signaturschlüssel, sodass die Rotation keine Ausfallzeit verursacht.
  • Abrechnung nach Verbrauch, die Sie überprüfen können.Speicher als Durchschnitt und nicht als Höchstwert, Verkehr als die tatsächlich gelieferten Bytes, Operationen einzeln gezählt — im Dashboard nach Tag aufgeschlüsselt.
  • Eine API, die zugibt, was sie getan hat.Jede Antwort sagt, was gemacht wurde und was übersprungen wurde, und es gibt einen trockenen Abgleich-Endpunkt zur Überprüfung einer Migration in Chargen.

04Was wir gemacht haben

keepix zerfällt in einige klare Teile, von denen jeder eine Aufgabe hat:

  • Eingang.Ein unterzeichneter Endpunkt akzeptiert das Original mit seiner Art, seinem Schlüssel und der Liste der gewünschten Varianten. Bilder kommen im selben Antwortformat zurück; Videos gehen in eine Warteschlange und die Antwort sagt das.
  • Die Verarbeitungs-Pipeline.Typenerkennung, Virenscan, Neukodierung, Metadatenentfernung, Variantenbildung — in einer festen Reihenfolge, die pro Anfrage nicht übersprungen werden kann.
  • Lieferung.Ein separater Host ohne Sitzungen, ohne JavaScript und kein Set-Cookie, der Dateien direkt von der Festplatte mit einem einjährigen unveränderlichen Cache bereitstellt.
  • Die Tenant-API.Unterzeichnet mit HMAC-SHA256, mit Endpunkten zum Auflösen von Adressen, zum Abgleichen in Chargen, zum Exportieren eines Inventars, zum Löschen eines Objekts und zum Löschen aller Daten, die einer Person gehören.
  • Das Dashboard.Ordner, Drag-and-Drop, Suche nach Titel, Beschreibung und Tags — und Nutzung aufgeschlüsselt nach Tag. Es funktioniert vollständig ohne JavaScript; das Skript fügt Komfort hinzu, nicht die Fähigkeit zu arbeiten.
  • Abrechnung.Pakete plus gemessene Überlast, gehalten in Hundertsteln eines Cents, mit einer Unterbrechung der Aufnahme, anstatt stillschweigend Geld im kostenlosen Plan auszugeben.
  • Der Bestand.Eine Marktplatzschicht, in der Mieter ihre eigenen Arbeiten kaufen und verkaufen können, wobei derselbe Speicher- und Lieferweg geteilt wird.

Alles unten ist ein näherer Blick auf die Entscheidungen innerhalb dieser Teile — die, die später teuer zu ändern wären.

05Lösungsarchitektur

Der Service besteht aus drei Räumen mit physisch getrennten Eingängen, und diese Trennung ist das Rückgrat des gesamten Designs:

  • Öffentliche Bereitstellung — überhaupt keine API.Der Webserver sucht die Datei auf der Festplatte und liefert sie aus. Nicht gefunden bedeutet 404, nicht "on the fly generieren". Es gibt keinen ausführbaren Code auf diesem Pfad, weshalb der Bereitstellungs-Host default-src 'none'; sandbox und das ernst meint.
  • Die private API des Mieters. Jede Anfrage ist signiert; die Signatur umfasst die Methode, den Pfad mit seiner Abfragezeichenfolge, den Zeitstempel und einen Hash des Körpers. Den Pfad ohne die Abfrage zu signieren würde Art und Schlüssel unsigned — austauschbar, und niemand würde es bemerken.
  • Die Admin-API für das Kontrollpanel, an einem eigenen Einstiegspunkt mit eigenen Anmeldeinformationen.

Antworten sind immer JSON und immer auf Englisch, mit einer Formulierung pro Ablehnungscode — sodass der gleiche Fehler in jedem Protokoll des Clients gleich aussieht, egal welche Sprache ihr Produkt spricht.

Innerhalb davon teilt sich der Speicher in einen kalten Baum der Originale, der niemals nach außen bereitgestellt wird, und den bereitgestellten Baum der Varianten. Das Behalten des Originals macht es möglich, ein neues Variant ein Jahr später zu erstellen, ohne den Kunden zu bitten, erneut etwas hochzuladen — und es ist auch der Grund, warum "wir stellen die Datei, die Sie gesendet haben, nicht zur Verfügung" eine absolute Regel sein kann, anstatt eine übliche.

06Das Leben einer Datei

Eine Datei durchläuft eine feste Sequenz, und die Reihenfolge ist entscheidend:

  • Der Typ wird durch Parsing bestimmt — aus dem Inhalt, nicht aus der Erweiterung und nicht aus dem, was der Client sagt, dass es ist. Die Erweiterung der Ausgabedatei ist dem Dienst zuzuordnen.
  • Der Virenscan läuft, und er kann ablehnen. Wenn der Scanner nicht erreichbar ist, antwortet die Aufnahme 503 anstatt die Datei durchzulassen. Eine Datei stillschweigend durchzulassen, während die Überprüfung ausfällt, ist eine Lüge über die geleistete Arbeit, und es ist die Art von Lüge, die erst viel später ans Licht kommt.
  • Das Bild wird neu codiert. Kein Byte der angekommenen Datei wird jemals wieder ausgegeben.
  • Metadaten überstehen die Neukodierung nicht. EXIF und GPS sind verschwunden, weil der Pfad so aufgebaut ist, nicht weil derjenige, der hochgeladen hat, sich daran erinnert hat, sie zu entfernen.
  • Die Varianten werden erstellt und ihre Adressen werden in derselben Antwort zurückgegeben.

SVG wird weder akzeptiert noch bereitgestellt — niemals, auf dem Weg hinein oder auf dem Weg hinaus. Die Überprüfung erfolgt zweimal, sowohl auf dem deklarierten Inhaltstyp als auch auf den ersten Bytes der Datei, da ein Typ-Sniffer für SVG oft einfach "text" sagt. Was bereitgestellt wird, sind WebP, JPEG, PNG, MP4, eine HLS-Wiedergabeliste und ihre Segmente; alles andere ist ein 415.

Die Aufnahme ist idempotent: dasselbe sha256 unter demselben Schlüssel führt die Arbeit nicht erneut aus und gibt dieselben Adressen zurück. Wiederholungen nach einer unterbrochenen Verbindung sind daher kostenlos, was wichtig ist, wenn die Datei zwei Gigabyte groß ist und das Netzwerk ein Telefon ist.

07Benannte Varianten, keine willkürlichen Breiten

Bilder haben drei benannte Breiten — Daumen 400, Karte 1000, voll 1400, alle WebP mit Qualität 82 — und Sie fragen nach einer Variante beim Namen. Das Set ist endlich und pro Dateityp deklariert. Das ist die Entscheidung, auf der der gesamte Dienst basiert, und sie beseitigt eine Klasse von Problemen anstelle eines Fehlers.

Zwei Regeln bestimmen, wann eine Variante legitim nicht erscheinen darf, und beide existieren, um den Leser nicht zu belügen:

  • Nichts wird gestreckt. Wenn das Original schmaler ist als die Breite der Variante, bleibt die Variante in der Größe des Originals und die Antwort sagt das ehrlich — die w und h Felder tragen die echten Zahlen. Eine vergrößerte Kopie sieht aus wie ein Fehler des Dienstes, und das wäre es auch.
  • Identische Kopien werden nicht zweimal gespeichert.Wenn zwei Breiten die exakt gleiche Datei erzeugen würden, wird die zweite Adresse nicht erstellt. Zwei byte-identische Dateien an zwei Adressen sind zwei Downloads desselben auf das Telefon einer Person.

Was bedeutet, dass die Antwort gelesen werden muss, nicht angenommen: Sie können nach zwei Varianten fragen und eine erhalten, mit einem übersprungenen Block, der erklärt, warum. Wir haben das zu einer normalen, erwarteten Antwort gemacht, anstatt zu einem Ausnahmefall, da eine Integration, die gegen die optimistische Struktur geschrieben wurde, beim ersten kleinen Bild, das ein Benutzer hochlädt, fehlschlägt.

Das Hinzufügen einer neuen Variante später erfordert nicht, dass irgendetwas erneut hochgeladen wird: Sie wird aus dem Original im kalten Baum erstellt. Das ist der ganze Grund, warum das Original aufbewahrt wird.

08Video: eine HLS-Leiter, ein Poster und eine Vorschau

Ein Video-Upload wird zu einem Set, denn das ist es, was ein Player tatsächlich benötigt: eine HLS Treppe von bis zu drei Qualitätsstufen (640, 1280 und 1920 px), ein Poster Bild, das bei 10% der Dauer aufgenommen wird, ein stummes Vorschau Clip der ersten Sekunden für Hover- und Feed-Autoplay, und ein progressives MP4 als Fallback für ältere Clients.

Ein Schritt breiter als das Original wird nicht gemacht, aus demselben Grund, aus dem ein gestrecktes Bild nicht gemacht wird: Eine hochskaliertes Kopie sieht aus wie ein Fehler des Dienstes. Das Poster wird bei einem Zehntel der Dauer anstelle des ersten Frames aufgenommen, da der erste Frame eines echten Videos sehr oft schwarz ist.

Die Videoverarbeitung erfolgt nicht innerhalb der Anfrage, die die Datei akzeptiert hat — sie geht in eine Warteschlange, und die Antwort sagt das auch. Die Eingangsantwort ist ehrlich darüber, welche Teile jetzt bereit sind und welche noch ausstehen, sodass ein Client das, was er hat, speichern und später erneut anfragen kann, anstatt den Upload-Bildschirm eines Benutzers während einer Transcodierung zu blockieren.

09Ihre eigene Domain und ein ewiger Cache

Dateien werden von der eigenen Subdomain des Mieters bereitgestellt — img.theirdomain — auf die der Mieter mit einem A-Eintrag auf den Dienst verweist. Der Leser sieht seine Marke und seine Adresse und sieht keepix überhaupt nicht. Im kostenlosen Plan wird die Domain geteilt; beide Wege funktionieren gleich.

Ein Mieter kann so viele Domains verbinden, wie er möchte, aber genau eine kanonische eine, und diese Einschränkung ist absichtlich: Mit zwei kanonischen Domains erhält eine Datei zwei Adressen, was bedeutet, dass zwei Caches und dieselben Bytes zweimal auf das Telefon des gleichen Lesers heruntergeladen werden.

Die Lieferheader sind für jeden Mieter gleich und ändern sich nicht: ein Jahr lang unveränderlich Cache, nosniff, default-src 'none'; sandbox, eine permissive CORS-Richtlinie zum Lesen, und nicht ein einziges Set-Cookie. Der ewige Cache ist genau deshalb sicher, weil die Adresse einen Hash des Inhalts trägt: Ändern Sie die Bytes und die Adresse ändert sich mit ihnen, sodass ein ersetztes Bild den Leser sofort erreicht, anstatt auf eine TTL zu warten.

Und genau deshalb kann die Adresse nicht auf der Client-Seite berechnet werden. Sie wird vom Dienst zurückgegeben, neben dem Datensatz gespeichert, zu dem sie gehört, und erneut über resolve angefordert, falls sie jemals verloren geht.

10Eingang: Typ, Virenscan, Metadaten

Alles im Dienst ist eine Datei, die jemand gesendet hat – ein Foto in einer Bewertung, ein Walkthrough-Video. Die Aufnahmebedingungen sind zu ihrem Wohl und nicht der Form halber geschrieben, und jede von ihnen existiert, weil die Alternative leise scheitert.

Die Bestimmung des Typs durch Parsingstatt durch die Erweiterung ist keine Pedanterie: die Erweiterung ist das eine, was ein Uploader vollständig kontrolliert.Die Ablehnung, wenn der Scanner nicht funktioniertkostet Verfügbarkeit und kauft die Garantie, dass "gescannt" gescannt bedeutet.Jedes Bild neu zu kodierenbedeutet, dass eine gestaltete Datei niemals in ihrer ursprünglichen Form einen Leser erreicht.Metadaten während dieser Neucodierung entfernen bedeutet, dass es nicht vergessen werden kann.

Auf der Lieferseite sind die Garantien strukturell und nicht prozedural. Der Lieferhost hat keine Sitzungen, kein JavaScript und keinen ausführbaren Code auf dem Weg; Inhalte werden mit einer Inhalts-Sicherheitsrichtlinie ausgegeben, die einen Browser daran hindert, irgendetwas auszuführen, selbst wenn etwas Gefährliches eingedrungen ist. Zwei unabhängige Schichten, denn die interessanten Fehler sind die, bei denen die erste Schicht falsch konfiguriert war und niemand es bemerkt hat.

Das Lieferprotokoll, mit den Adressen der Leser, lebt 24 Stunden. Nichts im Dienst benötigt es länger, was das längere Aufbewahren zu einer bloßen Ansammlung der Spuren anderer Menschen macht.

11Alles über eine Person löschen

Jedes Objekt trägt ein owner_ref — wie auch immer der Mieter den Eigentümer dieser Datei nennt. Für Katalogbilder ist es leer: sie beziehen sich nicht auf eine Person. Für Bewertungsfotos und Benutzer-Videos ist es erforderlich.

Eine Anfrage löscht alles darunter, und die Antwort nennt eine Zahl: wie viele Objekte und wie viele Dateien gegangen sind. Diese Zahl ist der Punkt des Endpunkts. Ein persönlicher Datentor sollte die Ausführung überprüfen, nicht die Absicht — eine als gelöscht markierte Zeile, während die Bytes noch auf der Festplatte liegen, ist eine Löschung, die nicht stattgefunden hat, und sie wird nicht stattfinden, bis jemand hinsieht.

Löschung entfernt Aufzeichnungen und Bytes auf einmal, anstatt nach einem Zeitplan, und sie nimmt die verschobenen Kopien früherer Versionen mit sich. Es ist auch das Stück, das keepix von einem Produkt mit eigenen GDPR-Verpflichtungen nutzbar macht: ein Löschfluss im Code des Clients kann es inline aufrufen und die Anzahl zurücklesen, anstatt darauf zu vertrauen, dass ein Bereinigungsjob irgendwo es erledigt.

Signierungsschlüssel funktionieren auf die gleiche Weise.Ein Mandant hat immer zwei im Einsatz, denn bei einer Schlüsselrotation bedeutet das Ausfallzeit — und Ausfallzeit ist der Grund, warum die Rotation für immer verschoben wird. Das Geheimnis wird einmal bei der Ausgabe angezeigt und niemals an das Kontrollpanel zurückgegeben: die Fähigkeit, eine Anfrage zu signieren, ist das Recht, in den Speicher eines anderen zu schreiben.

12Eine signierte API und Abgleich

Jede private Anfrage trägt vier Header — Mandant, Schlüssel-ID, Zeitstempel und Signatur — und die Signatur ist HMAC-SHA256 über die Methode, den vollständigen Pfad mit Abfrage, den Zeitstempel und den Hash des Körpers. Das Zeitfenster beträgt 300 Sekunden. Eine identische signierte Anfrage, die innerhalb derselben Sekunde wiederholt wird, wird als Wiederholung abgelehnt, was der Schutz ist, der wie beabsichtigt funktioniert, und der Vertrag sagt das klar, anstatt Integratoren es selbst herausfinden zu lassen.

Die Endpunktgruppe ist klein und jeder beantwortet eine echte betriebliche Frage:

  • setzen — ein Original akzeptieren; setzen-variante — eine bereits fertige Kopie akzeptieren, ohne sie neu zu kodieren, um das zu übertragen, was ein Kunde bereits hat. Eine fertige Kopie neu zu kodieren bedeutet, Qualität umsonst zu verlieren.
  • resolve und auflösen-charge (bis zu 1000 Schlüssel) — welche Adressen dieser Schlüssel hat. Dies ist ein Reparaturwerkzeug: Wenn ein Datensatz verloren geht, fragt man lieber, als zu raten.
  • abgleichen — eine Trockenprüfung, bis zu 1000 Datensätze gleichzeitig, die nichts ändert und beantwortet ok / fehlend / Mismatch / ausstehend pro Datensatz plus einen Digest des Batches.
  • Inventar — der Export, der den Vergleich in die andere Richtung durchführt, die Liste des Dienstes gegen die des Kunden, paginiert nach dem Schlüssel selbst, sodass die Seite nicht verrutscht, während Dinge während des Durchlaufs akzeptiert werden.
  • Objekt, löschen, Kontingent, Ping, und ein nicht signierter healthz der sagt, dass das Frontend lebt und nichts weiter — keine Version, kein Datenbankzustand.

Die Rekoncilierung verdient eine eigene Notiz, denn sie ist der Endpunkt, der den gesamten Vertrag rechtfertigt. Nach einer großen Migration gibt es genau eine Frage, die zählt: Hat alles funktioniert? Es einzeln zu beantworten bedeutet so viele Anfragen zu stellen, wie es Dateien gibt, also macht es niemand — und dann wird eine Migration als abgeschlossen erklärt, basierend darauf, dass das Upload-Skript nicht abgestürzt ist. Batch-Rekoncilierung macht die ehrliche Antwort günstig genug, um sie tatsächlich zu erhalten.

13Dashboard, Nutzung und Abrechnung

Das Dashboard ist eine Bibliothek ohne jeglichen Code: Ordner, Dateien mit der Maus ziehen, Suche nach Titel, Beschreibung und Tags. Es funktioniert vollständig ohne JavaScript — das Skript fügt Komfort hinzu, nicht die Fähigkeit zu arbeiten. Das ist keine Nostalgie; es ist der günstigste Weg, um zu garantieren, dass das Panel bei einer schlechten Verbindung benutzbar bleibt und ohne einen Browser-Engine testbar ist.

Die Nutzung wird ab Beginn des Monats gezählt und nach Tag aufgeschlüsselt: wie viel Speicher, wie viel Lieferverkehr und wie viele Operationen an jedem Datum, wie viel vom Paket verbraucht wird und was oben drauf kommt. Die Aufschlüsselung ist wichtiger als die Gesamtsumme — eine Rechnung, die du keinem Tag zuordnen kannst, ist eine Rechnung, mit der du nicht argumentieren kannst.

Drei Entscheidungen in der Abrechnung sind erwähnenswert:

  • Der Speicher ist der Durchschnitt über den Zeitraum, nicht der Höchstwert.Ein einzelner Tag mit einem großen Upload sollte nicht den gesamten Monat berechnen.
  • Der Lieferverkehr sind die tatsächlich gelieferten Bytes, entnommen aus dem Serverprotokoll – nicht "Dateigröße × Anzahl der Anfragen", was jede Bereichsanfrage und jeden abgebrochenen Download überbewertet.
  • Die Preise werden in Hundertsteln eines Cents gehalten.Ein Gigabyte kann weniger als einen Cent kosten; das Runden auf ganze Cents würde die gesamte gemessene Rechnung aufbrauchen und die Rechnung stillschweigend in Fiktion verwandeln.

Im kostenlosen Plan gibt es überhaupt keine gemessene Abrechnung: Die Aufnahme stoppt, anstatt stillschweigend zu beginnen, das Geld des Kunden auszugeben. Ein Dienst, der eigenständig beschließt, jemanden abzurechnen, der nie eine Karte eingegeben hat, ist ein Dienst, den niemand zweimal empfiehlt.

14Technologie-Stack

Wir haben den Stack für einen Dienst gewählt, dessen Hot Path ein Webserver ist, der eine Datei übergibt:

  • PHP 8.4 für die API und das Kontrollpanel — ausgereift, schnell genug auf diesem Pfad und kostengünstig im Betrieb, was für ein Produkt mit einem kostenlosen Tarif wichtig ist.
  • Ein dedizierter Lieferhost der direkt von der Festplatte bedient, ohne dass Anwendungscode im Anfragepfad vorhanden ist.
  • WebP für Bilder in fester Qualität, H.264 + AAC in HLS für Video mit einem progressiven MP4-Fallback.
  • Eine Warteschlange für Videoarbeiten, sodass ein Transcode niemals innerhalb einer Upload-Anfrage sitzt.
  • HMAC-SHA256 Anfrageunterzeichnung mit zwei aktiven Schlüsseln pro Mandant und einem Zeitfenster von 300 Sekunden.
  • Ein kalter Baum von Originalen außerhalb des bereitgestellten Pfades, was neue Varianten und Wiederaufbauten später möglich macht.

Der Dienst ist absichtlich konservativ in dem, worauf er angewiesen ist. Er macht keine eigenen ausgehenden Anfragen, verwendet kein Drittanbieter-CDN und hält keine Aufzeichnungen über die Benutzer des Clients über Mandant, Schlüssel, Art, Byte-Anzahl und Varianten hinaus. Jede Abhängigkeit, die Sie nicht haben, ist eine Abhängigkeit, die Ihre Medien an einem Dienstag nicht brechen kann.

15Design und UX

Die öffentliche Seite hatte eine Aufgabe: ein Infrastrukturprodukt für den Ingenieur verständlich zu machen, der es tatsächlich integrieren wird. Daher zeigt der erste Bildschirm die echte Anfrage und die echte Antwort – die Adressen, die in JSON zurückkommen – anstatt eine Abstraktion davon. Ein Integrator entscheidet in etwa dreißig Sekunden, ob ein Medienservice zu seinem Code passt, und sie entscheiden, indem sie sich die Form der Antwort ansehen.

Der Rest der Seite ist auf die gleiche Weise geschrieben. Jede Fähigkeit wird zusammen mit ihrer Einschränkung angegeben: benannte Varianten undwarum willkürliche Breiten abgelehnt werden; ein ewiger Cacheundder Hash in der Adresse, der sie sicher macht; ein Löschendpunktunddie Nummer, mit der geantwortet wird. Dokumentation, die nur auflistet, was ein Dienst tun kann, lässt den Leser die Grenzen in der Produktion entdecken.

Im Dashboard verwandelt sich dasselbe Prinzip in die No-JavaScript-Baseline und die tägliche Nutzungsaufteilung: nichts auf dem Bildschirm, was der Leser nicht überprüfen kann, und nichts, das nicht funktioniert, wenn ein Skript nicht geladen werden kann.

16Wie wir gearbeitet haben

Das Projekt lief in Phasen, die jeweils in etwas Nachweisbarem endeten:

  • Vertrag zuerst.Wir haben den API-Vertrag geschrieben – Signierungsschema, Endpunkte, Ablehnungscodes, Antwortformen – bevor die Implementierung, denn der Vertrag ist der Teil, gegen den die Kunden aufbauen, und der Teil, der teuer zu ändern ist.
  • Aufnahme und Varianten.Die Pipeline, in ihrer festen Reihenfolge, mit den zwei Regeln, nicht zu dehnen und keine Duplikate zu speichern, von Anfang an eingebaut.
  • Lieferung.Der separate Host, das gesetzte Header, die inhalts-Hash-basierte Adressierung, die den ewigen Cache sicher macht.
  • Video.Die Leiter, das Plakat und die Vorschau sowie die Warteschlange, die weiterhin aus der Upload-Anfrage transkodiert.
  • Multi-Tenancy.Domains, Arten, Varianten, Obergrenzen und das Zwei-Schlüssel-Rotationsmodell.
  • Abrechnung und Dashboard.Abgerechnete Zählung mit einer täglichen Aufschlüsselung, dann die Bibliotheksbenutzeroberfläche darüber.
  • Eine echte Migration.Wir haben einen Live-Katalog auf den Dienst übertragen und in Chargen abgeglichen – so hat der Abgleich-Endpunkt seine Form erhalten.

Diese letzte Phase war mehr wert als jede Menge interner Tests. Das Verschieben von Hunderttausenden echten Dateien hat Dinge gefunden, die eine Test-Suite nicht erkennt: Objekte, die der Dienst akzeptiert und verloren hat, Datensätze, deren gespeicherte Adresse abgedriftet ist, und der Unterschied zwischen einer Überprüfung, die Hashes verifiziert, und einer Überprüfung, die lediglich bestätigt, dass eine Zeile existiert. Eine Überprüfung ohne etwas, das zu verifizieren bleibt, meldet Erfolg – also haben wir den schwachen Modus so gestaltet, dass er angibt, in welchem Modus er sich befindet.

17Das Ergebnis

Was herauskam, ist ein Medienhost, auf den ein Produkt sich stützen kann: eine signierte Anfrage hinein, fertige Adressen heraus, bereitgestellt von der Domain des Kunden mit einem Cache, der für immer gehalten werden kann.

1 Anfrage
original hinein – fertige Adressen zurück
0
ausgehende Anfragen: der Dienst kennt keine Drittanbieteradresse
24 h
das Lieferprotokoll mit den Adressen der Leser lebt einen Tag
  • Benannte Varianten und eine HLS-Leiter, einmal gebaut und für immer hinter einer inhaltshashten Adresse zwischengespeichert.
  • Die eigene Subdomain des Kunden: der Leser sieht seine Marke, nicht unsere.
  • Sicherheit, die in den Eingabepfad integriert ist — analysierter Typ, verweigerter Virenscan, Neu-Codierung, entfernte Metadaten, SVG an beiden Enden verweigert.
  • Löschung, die mit einer Zahl antwortet, sodass ein persönlicher Datengate die Ausführung anstelle der Absicht überprüfen kann.
  • Multi-Tenancy mit eigenen Domains, eigenen Obergrenzen und einer Zwei-Schlüssel-Rotation ohne Ausfallzeiten.
  • Abrechnung nach Verbrauch, die in Hundertsteln eines Cents gehalten wird, aufgeschlüsselt nach Tag, und ein kostenloser Plan, der stoppt, anstatt zu verbrauchen.

18Erkenntnisse

keepix ist ein Fall, bei dem der Großteil der Ingenieursarbeit darin bestand, zu entscheiden, was der Dienst verweigern würde. Keine willkürlichen Breiten, keine berechneten Adressen, kein SVG, keine ausgehenden Anfragen, keine Beibehaltung der ursprünglichen Bytes auf dem bereitgestellten Pfad, keine Abrechnung im kostenlosen Plan. Jede dieser Maßnahmen entfernt eine ganze Klasse von Fehlern, anstatt einen zu behandeln.

Die wichtigste Lektion, die wir zum nächsten Infrastrukturprojekt mitnehmen würden: die ehrliche Antwort günstig zu erhalten. Abgleich in Chargen, Nutzung nach Tag aufgeschlüsselt, ein Löschvorgang, der eine Zählung zurückgibt, eine Eingangsantwort, die benennt, was übersprungen wurde — in jedem Fall war die Alternative nicht eine falsche Antwort, sondern gar keine Antwort, und keine Antwort ist das, womit Teams stillschweigend zufrieden sind.

Wenn Sie einen Medienservice unter Ihrer eigenen Domain benötigen, eine Datei-Pipeline, die sicher mit den Uploads anderer umgehen muss, oder ein API-first-Produkt, bei dem der Vertrag wichtiger ist als die Bildschirme, dann ist das die Art von Arbeit, die wir von Anfang bis Ende machen.