Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung Künstliche Intelligenz 22.10.2026 · 13 min Lesezeit

Hindsight 1.1 mit Docker: Gedächtnis für KI-Agenten selbst hosten

Hindsight 1.1.0 gibt KI-Agenten ein dauerhaftes Gedächtnis. So betreiben Sie den Server per Docker Compose mit PostgreSQL, lokalem Sprachmodell über llama.cpp, Schlüsselschutz, MCP-Anbindung, Backup, Wiederherstellung und Update.

Geprüft am 22.10.2026 · für Hindsight 1.1.0

WerbelinksMit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Mehr dazu

Titelbild: Hindsight, Gedächtnis für KI-Agenten mit Docker Compose

KI-Agenten vergessen nach jeder Sitzung, was sie gelernt haben. Hindsight von Vectorize ist ein Gedächtnis-Server dagegen: Ein Sprachmodell zieht Fakten aus abgelegtem Wissen, bei der nächsten Frage kommen sie zurück. Das Projekt steht unter MIT-Lizenz und hat über 52.500 Sterne auf GitHub (Stand 22. Oktober 2026). Diese Anleitung richtet Version 1.1.0 mit Docker Compose und lokalem Sprachmodell ein, die Texte verlassen das eigene Netz nicht. Ohne Grafikkarte dauerte das Merken einer Notiz im Test 102 bis 157 Sekunden.

Voraussetzungen

  • Linux-Server oder VM mit Docker Engine und Docker Compose Plugin, getestet mit Docker 29.8.2 und Compose 5.5.1 unter Debian 12. Die Grundinstallation zeigt die Anleitung Docker Compose unter Linux.
  • Die Werkzeuge curl, jq, openssl und unzip, unter Debian mit sudo apt install curl jq openssl unzip.
  • Eine x86-64-CPU mit möglichst vielen Kernen, getestet mit Intel Core i7-12700T (12 Kerne) ohne Grafikkarte. Das Projekt sieht CPU und kleines Modell als Funktionstest und empfiehlt für den echten Einsatz eine GPU und ein Modell mit 7 bis 13 Milliarden Parametern.
  • Mindestens 8 GB RAM, mit den deutschen Modellen aus den Häufigen Fragen 16 GB. Im Vergleichstest belegten die drei Container zusammen 3,3 GiB, mit deutschen Modellen 8,4 GiB.
  • Mindestens 15 GB freier Speicher: Images rund 5,6 GB, Modellordner rund 4,5 GB, beim Update das alte Image. Deutsche Modelle brauchen weitere 6,9 GB.
  • Ein Reverse Proxy mit HTTPS auf demselben Server, zum Beispiel Caddy.
EckdatenWert
Imageghcr.io/vectorize-io/hindsight:1.1.0
Sprachmodellghcr.io/ggml-org/llama.cpp:server-b11804 mit Gemma 4 E2B (Q4_K_M)
Ports8888/tcp (API und MCP), 9999/tcp (Weboberfläche), beide nur 127.0.0.1
Architekturx86-64 getestet, Images auch für ARM64

Schritt 1: Projektordner, Schlüssel und compose.yaml anlegen

Erzeugen Sie Datenbankpasswort, API-Schlüssel und Zugangsschlüssel zufällig in einer .env. Der Backup-Ordner gehört UID 1000, dem Benutzer von Hindsight im Container.

mkdir -p ~/hindsight/backups && cd ~/hindsight
sudo chown 1000:1000 backups
cat > .env <<EOF
HINDSIGHT_VERSION=1.1.0
HINDSIGHT_DB_PASSWORD=$(openssl rand -hex 24)
HINDSIGHT_API_KEY=$(openssl rand -hex 32)
HINDSIGHT_CP_ACCESS_KEY=$(openssl rand -hex 16)
EOF
chmod 600 .env

Erzeugen Sie die .env nur einmal, PostgreSQL übernimmt das Passwort nur beim ersten Start. Daneben kommt die compose.yaml:

services:
  db:
    image: pgvector/pgvector:0.8.7-pg18
    restart: unless-stopped
    environment:
      POSTGRES_USER: hindsight
      POSTGRES_PASSWORD: ${HINDSIGHT_DB_PASSWORD:?HINDSIGHT_DB_PASSWORD fehlt in .env}
      POSTGRES_DB: hindsight
    volumes:
      - ./data/postgres:/var/lib/postgresql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U hindsight -d hindsight"]
      interval: 10s
      timeout: 5s
      retries: 12

  llama:
    image: ghcr.io/ggml-org/llama.cpp:server-b11804
    restart: unless-stopped
    environment:
      LLAMA_ARG_HOST: 0.0.0.0
      LLAMA_ARG_PORT: "8080"
      LLAMA_ARG_HF_REPO: bartowski/google_gemma-4-E2B-it-GGUF
      LLAMA_ARG_HF_FILE: google_gemma-4-E2B-it-Q4_K_M.gguf
      LLAMA_ARG_CTX_SIZE: "16384"
      LLAMA_ARG_N_PARALLEL: "2"
    volumes:
      - ./data/models:/root/.cache/huggingface
    healthcheck:
      test: ["CMD-SHELL", "curl -fsS http://localhost:8080/health || exit 1"]
      interval: 10s
      timeout: 5s
      retries: 90
      start_period: 30s

  hindsight:
    image: ghcr.io/vectorize-io/hindsight:${HINDSIGHT_VERSION:-1.1.0}
    restart: unless-stopped
    shm_size: 1g
    depends_on:
      db:
        condition: service_healthy
      llama:
        condition: service_healthy
    ports:
      - "127.0.0.1:8888:8888"
      - "127.0.0.1:9999:9999"
    environment:
      HINDSIGHT_API_DATABASE_URL: postgresql://hindsight:${HINDSIGHT_DB_PASSWORD}@db:5432/hindsight
      HINDSIGHT_API_LLM_PROVIDER: openai
      HINDSIGHT_API_LLM_BASE_URL: http://llama:8080/v1
      HINDSIGHT_API_LLM_API_KEY: not-needed
      HINDSIGHT_API_LLM_MODEL: gemma-4-e2b-it
      HINDSIGHT_API_LLM_TIMEOUT: "600"
      HINDSIGHT_API_LLM_MAX_CONCURRENT: "2"
      HINDSIGHT_API_WORKER_ID: hindsight-prod
      HINDSIGHT_API_TENANT_EXTENSION: hindsight_api.extensions.builtin.tenant:ApiKeyTenantExtension
      HINDSIGHT_API_TENANT_API_KEY: ${HINDSIGHT_API_KEY:?HINDSIGHT_API_KEY fehlt in .env}
      HINDSIGHT_CP_DATAPLANE_API_KEY: ${HINDSIGHT_API_KEY}
      HINDSIGHT_CP_ACCESS_KEY: ${HINDSIGHT_CP_ACCESS_KEY:?HINDSIGHT_CP_ACCESS_KEY fehlt in .env}
    volumes:
      - ./backups:/backups

Hindsight schickt Merkaufträge und Hintergrundarbeit wie die Konsolidierung (Fakten werden zu Beobachtungen zusammengefasst) parallel an das Sprachmodell. HINDSIGHT_API_LLM_MAX_CONCURRENT begrenzt das auf zwei Anfragen (ab Werk 32), wie es die Dokumentation für ihren eingebauten llama.cpp-Anbieter empfiehlt. llama.cpp teilt den Kontext auf die Slots auf, hier 8.192 Token je Anfrage. Mit insgesamt 8.192 Token wie im Compose-Beispiel des Projekts brach im ersten Testlauf ein Merkauftrag ab (siehe Troubleshooting). HINDSIGHT_API_LLM_TIMEOUT hebt das Limit je Anfrage auf 600 Sekunden, die feste HINDSIGHT_API_WORKER_ID verhindert laut Dokumentation liegengebliebene Aufgaben nach Neustarts. Weitere Härtung beschreibt Docker Compose absichern.

Für deutsche Inhalte können Sie vor dem ersten Start die Einstellungen aus den Häufigen Fragen ergänzen.

Verifizieren: docker compose config -q gibt nichts aus. Fehlt ein Wert in der .env, bricht der Befehl mit dem Namen der Variable ab.

Schritt 2: Stack starten und Version prüfen

Beim ersten Start lädt llama.cpp das Modell, der Healthcheck wartet darauf höchstens etwa 15 Minuten. Reicht das nicht, brechen Sie mit Strg+C ab, prüfen docker compose logs llama und starten erneut.

docker compose up -d --pull always
until curl -sf http://127.0.0.1:8888/health >/dev/null; do sleep 5; done
docker compose ps
export HINDSIGHT_API_KEY=$(grep '^HINDSIGHT_API_KEY=' .env | cut -d= -f2)
curl -s -H "Authorization: Bearer $HINDSIGHT_API_KEY" http://127.0.0.1:8888/version | jq -r .api_version

In einer neuen Shell wiederholen Sie die export-Zeile im Projektordner. Im Test meldete /health spätestens 30 Sekunden nach dem Hindsight-Start 200, die Versionsabfrage 1.1.0.

Verifizieren: Alle drei Container stehen in docker compose ps auf „Up“, db und llama zusätzlich auf „healthy“. Ohne Schlüssel antwortet /v1/default/banks mit 401, mit falschem Schlüssel ebenfalls mit 401 und der Meldung {"detail":"Authentication failed: Invalid API key"}, mit richtigem Schlüssel mit 200.

Schritt 3: Weboberfläche öffnen und anmelden

Die Weboberfläche auf Port 9999 leitet ohne Anmeldung auf /login um. Erreichbar ist sie über den Reverse Proxy, etwa hindsight.example.com mit Ziel 127.0.0.1:9999 (ein Proxy im eigenen Container erreicht 127.0.0.1 des Hosts nicht), oder per SSH-Tunnel ssh -L 9999:127.0.0.1:9999 benutzer@server und http://localhost:9999. Melden Sie sich mit dem Wert von HINDSIGHT_CP_ACCESS_KEY an.

Anmeldeseite der Hindsight-Weboberfläche mit Feld für den Zugangsschlüssel und Knopf Anmelden
1 Sprache wechseln, 2 Zugangsschlüssel aus HINDSIGHT_CP_ACCESS_KEY eintragen, 3 Anmelden.

Das Dashboard zeigt alle Speicherbänke. Eine Bank ist ein abgeschotteter Speicher je Agent, Kunde oder Projekt, der API-Schlüssel gilt aber für alle Banks.

Hindsight-Dashboard mit der Speicherbank firmenwissen und vier Erinnerungen
1 Neue Bank erstellen, 2 Bank öffnen. Nach dem Test enthält firmenwissen vier Erinnerungen.

Verifizieren: Der richtige Zugangsschlüssel öffnet das Dashboard. Ein falscher führt zu einer Fehlermeldung, /api/auth/login antwortet dann mit HTTP 401 und {"error":"Invalid access key"}.

Schritt 4: Bank anlegen, Wissen merken und abrufen

Retain zieht mit dem Sprachmodell Fakten, Personen und Zeitangaben aus Text, Recall sucht sie laut Dokumentation per Vektor- und Stichwortsuche, Verknüpfungen und Zeitbezug, Reflect wertet sie gründlicher aus. Legen Sie eine Bank an und speichern Sie eine Notiz:

H="Authorization: Bearer $HINDSIGHT_API_KEY"
curl -s -X PUT http://127.0.0.1:8888/v1/default/banks/firmenwissen \
  -H "$H" -H "Content-Type: application/json" \
  -d '{"reflect_mission":"Wissen über die IT der Beispiel GmbH"}'
curl -s -X POST http://127.0.0.1:8888/v1/default/banks/firmenwissen/memories \
  -H "$H" -H "Content-Type: application/json" \
  -d '{"items":[{"content":"Die Beispiel GmbH sichert ihren Dateiserver jeden Abend um 22:30 Uhr auf ein NAS im Serverraum. Zuständig ist Frau Berger aus der Buchhaltung.","document_id":"backup-notiz"}]}'

Im Test dauerte das auf CPU 102 Sekunden (Version 1.0.0, gleiches Modell). Eine zweite Notiz zur Fernwartung (nur VPN mit Zwei-Faktor-Anmeldung, keine RDP-Freigaben ins Internet) brauchte unter 1.1.0 157 Sekunden, die Bilder zeigen beide. Danach fragen Sie ab:

curl -s -X POST http://127.0.0.1:8888/v1/default/banks/firmenwissen/memories/recall \
  -H "$H" -H "Content-Type: application/json" \
  -d '{"query":"Wann wird der Dateiserver gesichert?"}' | jq -r '.results[].text'
Die Beispiel GmbH sichert ihren Dateiserver jeden Abend um 22:30 Uhr auf ein NAS im Serverraum | When: jeden Abend um 22:30 Uhr | Involving: Beispiel GmbH
Frau Berger aus der Buchhaltung ist für die Sicherung des Dateiservers zuständig | Involving: Frau Berger | zuständig für die Sicherung des Dateiservers

Aus zwei Sätzen wurden zwei Fakten mit Person oder Firma, der Sicherungsfakt zusätzlich mit Zeitbezug. In der Oberfläche stehen sie unter „Erinnerungen“, die Tabelle ist übersichtlicher als die Ansicht „Konstellation“.

Erinnerungen der Bank firmenwissen in der Tabellenansicht mit vier Weltfakten und erkannten Entitäten
1 Erinnerungen öffnen, 2 Tabellenansicht wählen. Vier Fakten aus zwei Notizen, drei mit erkannten Entitäten.

Unter „Recall“ testen Sie Abfragen, im Test dauerte diese 0,06 Sekunden.

Recall-Analysator mit der Frage nach der Sicherung des Dateiservers und dem passenden Fakt an erster Stelle
1 Recall öffnen, 2 Frage eingeben, 3 Recall starten. Der Fakt mit 22:30 Uhr steht an erster Stelle.

Verifizieren: Die Antwort von Retain enthält "success":true und "items_count":1, Recall liefert die Uhrzeit 22:30 zurück.

Schritt 5: KI-Agenten per MCP anbinden

Über den eingebauten MCP-Server greifen Agenten wie Claude Code oder Cursor auf eine Bank zu. Für Claude Code zeigt die Dokumentation diesen Befehl, hier mit der Bank im Pfad statt im Header X-Bank-Id (Pfad laut Dokumentation vorrangig):

claude mcp add --transport http hindsight http://127.0.0.1:8888/mcp/firmenwissen/ \
  --header "Authorization: Bearer $HINDSIGHT_API_KEY"

Im Test meldete sich der Server unter dieser Adresse nach dem MCP-Handshake als hindsight-mcp-server 1.1.0 mit 36 Werkzeugen, recall nannte auf die Frage nach der Zuständigkeit für die Sicherung Frau Berger. Agenten auf anderen Rechnern brauchen eine eigene Subdomain mit Ziel 127.0.0.1:8888, die der Weboberfläche reicht nicht.

Verifizieren: Ohne Schlüssel antwortet /mcp/firmenwissen/ mit HTTP 401. Mit Schlüssel liefert der MCP-Handshake (initialize, danach tools/list) die Werkzeugliste mit retain, recall und reflect.

Schritt 6: Backup mit hindsight-admin

hindsight-admin backup schreibt alle Tabellen in eine ZIP-Datei im gemounteten Ordner backups, ohne den Dienst anzuhalten.

docker compose exec -T hindsight hindsight-admin backup /backups/hindsight-110.zip 2>&1 | tail -3
ls -la backups/
  [23/23] Backing up entity_maintenance_queue... 0 rows
Backed up 45 rows across 23 tables
Backup saved to /backups/hindsight-110.zip

Die .env gehört in den Passwortmanager, backups regelmäßig auf ein anderes System. Einen nächtlichen Cron-Eintrag legen Sie mit crontab -e als derselbe Benutzer an. -T verhindert, dass Compose ein Terminal erwartet, das Prozentzeichen ist für Cron maskiert:

30 23 * * * cd $HOME/hindsight && docker compose exec -T hindsight hindsight-admin backup /backups/hindsight-$(date +\%F).zip

Verifizieren: unzip -l backups/hindsight-110.zip listet je Tabelle eine .bin-Datei und eine manifest.json, im Test 24 Dateien.

Schritt 7: Wiederherstellung nach Totalverlust

Im Test wurde der Datenbankordner gelöscht. Bei einem echten Ausfall verschieben Sie ihn besser und prüfen vorher mit ls -lt backups/, ob eine aktuelle Sicherung da ist, und setzen deren Namen ein. Da Hindsight das Schema erst beim Start anlegt, wartet der Block auf die API:

docker compose down
sudo mv data/postgres data/postgres.defekt
docker compose up -d
until curl -sf http://127.0.0.1:8888/health >/dev/null; do sleep 5; done
docker compose exec -T hindsight hindsight-admin restore /backups/hindsight-110.zip --yes 2>&1 | tail -4
docker compose exec -T hindsight hindsight-admin repair-bank --all 2>&1 | tail -2
docker compose restart hindsight
until curl -sf http://127.0.0.1:8888/health >/dev/null; do sleep 5; done
  [23/23] Restoring entity_maintenance_queue... 0 rows
  Synchronizing identity sequences...
Restored 45 rows across 23 tables
Restore complete
  schema 'public': 1 bank(s) scanned, 0 present, 3 created, 0 dropped, 0 to-create (dry-run), 0 to-drop (dry-run), 0 failed
Done: 1 bank(s) scanned, 0 already present, 3 created, 0 dropped, 0 to-create (dry-run), 0 to-drop (dry-run), 0 failed

restore löscht vorher alle Daten im Ziel, --yes überspringt die Rückfrage (nötig mit exec -T). repair-bank --all legt laut Dokumentation fehlende Suchindizes an, sonst wird die Suche langsamer, teils lückenhaft. Ist der ganze Server weg, folgen Sie Schritt 1 mit der .env aus dem Passwortmanager, so bleiben alle Schlüssel gültig.

Verifizieren: Die Bank firmenwissen ist wieder da, und die Abfrage aus Schritt 4 liefert wieder die Uhrzeit 22:30. Im Test waren nach dem Restore alle vier Fakten aus beiden Notizen zurück.

Schritt 8: Update auf eine neue Version

Im Test lief das Update von 1.0.0 auf 1.1.0 mit vorhandenen Daten, Migrationen laufen beim Start. Setzen Sie statt X.Y.Z die neue Nummer aus den Release Notes ein:

docker compose exec -T hindsight hindsight-admin backup /backups/vor-update.zip
sed -i 's/^HINDSIGHT_VERSION=.*/HINDSIGHT_VERSION=X.Y.Z/' .env
docker compose pull hindsight
docker compose up -d
until curl -sf http://127.0.0.1:8888/health >/dev/null; do sleep 5; done
docker compose logs --no-log-prefix hindsight 2>&1 | grep -i migrat | tail -3

Die Ausgabe beim Update auf 1.1.0, ohne Zeitstempel am Zeilenanfang:

INFO - hindsight_api.migrations - Using vector extension: pgvector
INFO - hindsight_api.migrations - Target vector extension: pgvector
INFO - hindsight_api.engine.memory_engine - Schema migrations completed

Laut Dokumentation lohnt danach repair-bank --all aus Schritt 7. Bleiben Sie bei festen Nummern statt latest.

Verifizieren: /version meldet die neue Versionsnummer, und die Abfrage aus Schritt 4 findet die Notiz weiterhin. Im Test ließ sich nach dem Update sofort eine weitere Notiz speichern (157 Sekunden), der Aufruf meldete "success":true.

Troubleshooting

SymptomUrsache und Lösung
request (3401 tokens) exceeds the available context size (2048 tokens), try increasing it8.192 Token auf vier Slots ergeben 2.048 je Anfrage. Ohne feste Slots kam im ersten Testlauf Context size has been exceeded., weil zwei gleichzeitige Anfragen (3.408 und 4.845 Token) zusammen mehr als 8.192 belegten. Werte wie in der compose.yaml oben setzen.
Fact extraction failed: 1/1 chunks failed. First failures: chunk 0: APIConnectionError: Connection error.Sprachmodell nicht erreichbar, im Test nach 20 Sekunden bei gestopptem llama-Container. docker compose logs llama prüfen.
required variable HINDSIGHT_DB_PASSWORD is missing a value: HINDSIGHT_DB_PASSWORD fehlt in .envDie Zeile fehlt in der .env oder ist leer, Wert ergänzen.
{"detail":"Authentication failed: Invalid API key"}Falscher oder fehlender Bearer-Schlüssel, Wert von HINDSIGHT_API_KEY verwenden.
Fehlermeldung auf der Anmeldeseite, {"error":"Invalid access key"} an /api/auth/loginFür die Oberfläche gilt HINDSIGHT_CP_ACCESS_KEY, nicht der API-Schlüssel.
Bad Request: Missing session ID am MCP-EndpunktAufruf ohne MCP-Handshake, etwa ein einzelnes curl. Ein MCP-Client sendet zuerst initialize, danach die Sitzungs-ID.

Häufige Fragen

Funktioniert Hindsight mit deutschen Texten?

Ja. Ein Vergleichstest lief mit frischer Datenbank, denselben zwei Notizen und vier deutschen Fragen einmal mit den Standardmodellen und einmal mit deutschen Einstellungen. Nach Abschluss der Hintergrundarbeit fanden beide alle vier Antworten an erster Stelle, alle Fakten blieben deutsch. Dahinter lagen mit deutschen Einstellungen zweimal passendere Fakten. Im Stichwortindex behält das englische Wörterbuch ab Werk Füllwörter wie „die“ oder „ihren“, mit german fallen sie weg. Laut Dokumentation sind auch Embedding-Modell (macht Text zu Vektoren für die Ähnlichkeitssuche) und Reranker (sortiert Treffer) englisch, worunter die Ähnlichkeitssuche bei überwiegend deutschen Texten leidet. Für Deutsch legen Sie mit sudo mkdir -p data/hf && sudo chown 1000:1000 data/hf den Cache-Ordner an. Neben die compose.yaml kommt eine compose.override.yaml, die Compose automatisch mitliest:

services:
  hindsight:
    environment:
      HINDSIGHT_API_EMBEDDINGS_LOCAL_MODEL: BAAI/bge-m3
      HINDSIGHT_API_RERANKER_LOCAL_MODEL: BAAI/bge-reranker-v2-m3
      HINDSIGHT_API_TEXT_SEARCH_EXTENSION_NATIVE_LANGUAGE: german
      HINDSIGHT_API_LLM_OUTPUT_LANGUAGE: German
    volumes:
      - ./data/hf:/home/hindsight/.cache/huggingface

Der erste Start lud 6,9 GB Modelle und dauerte bis zur API 120 statt 20 Sekunden, nach dem Neuanlegen des Containers dank Cache-Ordner 25 Sekunden. Hindsight belegte 4,3 statt 0,9 GiB RAM, Abfragen dauerten 0,6 bis 1,4 statt rund 0,1 Sekunden, das Merken blieb ähnlich langsam. HINDSIGHT_API_LLM_OUTPUT_LANGUAGE erzwingt laut Dokumentation deutsche Fakten, im Haupttest formulierte das Modell einmal englisch. Entscheiden Sie vor dem ersten Eintrag, danach lässt sich die Vektorgröße laut Dokumentation nicht ohne Datenverlust ändern.

Geht es schneller als zwei Minuten pro Notiz?

Ja, mit Grafikkarte für llama.cpp oder einem Cloud-Modell, Hindsight unterstützt laut README über 25 Anbieter wie OpenAI, Anthropic und Ollama. Bei Cloud-Anbietern verlassen die Texte allerdings Ihr Netz, bei Kunden- oder Personaldaten eine Frage für den Datenschutz.

Worin unterscheidet sich Hindsight von Letta?

Letta ist ein kompletter Agenten-Server mit eigenem Gedächtnis. Hindsight ist nur das Gedächtnis und hängt sich per API oder MCP an beliebige Agenten, auch an solche mit Ollama.

Fazit

Hindsight gibt KI-Agenten ein Gedächtnis über Sitzungen hinweg, mit lokalem Sprachmodell ohne Daten nach außen. Das eingebaute Backup stellte im Test die gelöschte Datenbank vollständig wieder her. Auf CPU brauchte eine Notiz knapp zwei bis gut zweieinhalb Minuten, das passt zu einzelnen Einträgen, nicht zu laufendem Agentenverkehr. Wichtigste Stellschraube ist das Verhältnis von Kontext zu gleichzeitigen Anfragen. Die deutschen Modelle kosten viel RAM, bei vier Testfragen lagen dieselben Antworten vorn.

Weiterführende Anleitungen und Quellen

HindsightKI-AgentenDocker Composellama.cppMCPSelfhosting