Publier une application
Un POC dynamique en Node, Python ou PHP, un agent IA avec sa clé d’API, une base PostgreSQL : l’application se construit depuis son Dockerfile et tourne sous votre compte.
Conditions
- Un
Dockerfileà la racine du dossier envoyé. - L’application écoute sur
0.0.0.0, au port donné par la variablePORT, qui vaut 8080. - Elle dispose de 512 Mo de mémoire, d’un demi-processeur et de 256 processus. Un Space compte au plus 3 applications.
- L’image se construit et le conteneur tourne sous votre compte, sans droits d’administration ; les images comptent dans votre quota.
- L’application n’est joignable que par son adresse,
https://spaces.adalan.ai/durand/projet/.
Servie sous son chemin
Comme un site, l’application vit sous /durand/projet/. Le serveur retire ce préfixe avant de lui passer la requête : elle reçoit /, /api/items… comme si elle était à la racine. Le préfixe lui est donné de deux façons :
- la variable d’environnement
BASE_PATH, par exemple/durand/agent-rh; - l’en-tête
X-Forwarded-Prefixde chaque requête.
L’adresse de l’application, /durand/projet/, lui arrive comme / : c’est son serveur qui choisit la page d’accueil. Apache (images php:…-apache) et le serveur intégré de PHP (php -S 0.0.0.0:8080) ouvrent index.php, à défaut index.html, à la racine comme dans chaque dossier.
Dans ses pages et son JavaScript, les liens sont relatifs (api/items, static/app.css) ou préfixés par BASE_PATH, jamais /api/items. Le serveur ramène sous le chemin de l’application ses redirections vers /… et les cookies qu’elle pose pour /.
| Cadriciel | Réglage |
|---|---|
| FastAPI | FastAPI(root_path=os.environ.get("BASE_PATH", "")) |
| Flask | app.wsgi_app = ProxyFix(app.wsgi_app, x_prefix=1), puis url_for pour les liens |
| Express | Liens relatifs dans les gabarits et les appels fetch |
Deux Dockerfile de départ
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
# server.js : app.listen(process.env.PORT || 8080, "0.0.0.0")
CMD ["node", "server.js"]
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]
Secrets et réglages
Jamais de clé d’API dans le code ni dans l’image : le serveur refuse d’ailleurs les fichiers .env. Les variables se règlent à part, par l’entrée standard, pour que la valeur n’apparaisse dans aucune ligne de commande :
printf 'OPENAI_API_KEY=%s\n' "$CLE" | ssh adalan adalan env agent-rh --stdin
| Besoin | Commande |
|---|---|
| Lister les variables, sans leurs valeurs | ssh adalan adalan env agent-rh |
| Définir un réglage non secret | ssh adalan adalan env agent-rh MODELE=rapide |
| Retirer une variable | ssh adalan adalan env agent-rh --unset NOM |
Chaque changement relance l’application. Dans claude.ai, donnez la clé à Claude en lui demandant de la régler comme variable de l’application : il passe par l’outil set_app_env, qui ne renvoie jamais les valeurs.
Si l’application a besoin de sa clé pour démarrer, le premier envoi peut échouer faute de clé. Réglez la clé avec env --stdin : l’application se relance avec elle.
Envoyer
COPYFILE_DISABLE=1 tar -czf - -C dossier . | ssh adalan adalan deploy agent-rh --type docker
La construction peut prendre quelques minutes au premier envoi. La commande construit l’image, lance le conteneur et vérifie qu’il répond. Si la nouvelle version échoue, la précédente est remise en service et le journal s’affiche. --protect et --interne s’ajoutent comme pour un site.
Base de données
ssh adalan adalan db create agent-rh
La commande crée une base PostgreSQL dédiée, ajoute à l’application les variables DATABASE_URL, PGHOST, PGPORT, PGUSER, PGPASSWORD et PGDATABASE, puis la relance. L’application crée ses tables elle-même, au démarrage ou par ses migrations. Relancer la commande change le mot de passe de la base. Une base ne sert qu’une application : elle disparaît avec elle.
Application en accès interne
Publiée avec --interne, l’application ne reçoit que des visiteurs connectés : collaborateurs avec leur compte Microsoft adalan, freelances inscrits dans Grace avec un code reçu par e-mail. Chaque requête porte leur identité :
X-Adalan-Email: l’adresse du visiteur ;X-Adalan-Name: son nom, encodé en URL ;X-Adalan-Auth: sa connexion,microsoft(collaborateur) ouemail(freelance).
Ces en-têtes ne peuvent pas venir du navigateur : le serveur retire ceux qu’un client enverrait. L’application peut s’en servir pour personnaliser l’accueil, tracer les actions ou réserver une page aux collaborateurs, sans gérer de mots de passe. _adalan/sso/logout, sous l’adresse du site, déconnecte le visiteur.
Une application protégée par mot de passe reçoit seulement X-Adalan-Auth: password. Un script y accède avec curl -u adalan:mot-de-passe.
Diagnostic
ssh adalan adalan logs agent-rh --lines 100
| Symptôme | Que faire |
|---|---|
construction de l'image en échec | Lire les dernières lignes affichées (dépendance introuvable, commande en erreur), corriger, republier. |
la nouvelle version ne répond pas | Vérifier que l’application écoute sur 0.0.0.0 et le port PORT ; lire le journal. La version précédente reste en ligne. |
| Pages sans styles, appels d’API en 404 | Liens absolus dans les pages ou le JavaScript : les rendre relatifs, ou les préfixer par BASE_PATH. |
| Erreur 502 | L’application s’est arrêtée ou ne répond plus : lire le journal, puis republier. |
Le stockage du navigateur (localStorage) est commun à tous les sites de la plateforme, qui partagent la même adresse. Une application n’y range rien de confidentiel.