Skip to main content

Circe

API web pour la transformation de documents.

Table des matières

Description du service

Format d'échange

Le client fournit au serveur une tâche (un job) à effectuer sous forme d'une archive tar gzippée (*.tar.gz) contenant a minima à sa racine:

  • les fichiers à transformer
  • un fichier nommé job.json décrivant les transformations souhaitées sur ces fichiers

On peut également ajouter des fichiers utiles à la conversion, tel que des feuilles de styles ou des fichiers de fontes par exemple.

Structure du fichier job.json

Est placé dans l'archive un fichier job.json décrivant l'ensemble des opérations à faire sur les fichiers.

Un cas minimal:

{
    "transformations": [
        {"name": "html2pdf"}
    ]
}

... décrit une transformation unique à effectuer sur les documents fournis dans l'archive, sans options.

La seule clef obligatoire pour le job est la clef transformations, contenant la liste des transformations à faire. La seule clef obligatoire pour la transformation est la clef name, contenant le nom de la transformation.

Un cas plus complet:

{
    "transformations": [
        {"name": "html2pdf",
         "options": {"compression": 1}},
        {"name": "donothing",}
    ],
    "notify_hook": "http://www.domain.tld/notify-me/"
}

... décrit 2 transformations consécutives, dont une avec une option, ainsi qu'une URL de notification (notify_hook) qui sera appelée par le serveur à la fin du job.

Les résultats des transformations sont également fournis par le serveur sous forme d'archive tar gzippée.

Web API

GET /transformations/

Retourne une liste JSON des transformations supportées. Exemple:

["html2pdf","donothing", "docx2markdown"]

POST /job/

Attend dans le corps de la requête une archive de job correctement formée.

Retourne un UUID version 4 sous forme de chaîne de caractères. L'UUID retourné est l'identifiant du job, à conserver pour les prochaines requêtes.

Si l'option block=1 est passée dans l'URL (/job/?block=1), alors le comportement est différent: ce n'est pas l'UUID qui sera retourné mais directement le résultat des transformations, de manière identique à GET /job/[UUID].

En fonction de la configuration du serveur, la soumission d'un nouveau job peut nécessiter une authentification. Dans ce cas, l'entête HTTP Authorization doit être renseigné sous la forme suivante:

Authorization: [UUID de l'application] [signature HMAC de l'archive]

Voir le source du client Python pour un exemple de signature HMAC.

GET /job/[UUID]

Récupère l'archive contenant les fichiers transformés par le serveur. Exemple:

curl http://www.domain.tl/job/55d87fe0-3924-423a-893d-23aa45614ad9 

Un statut HTTP 200 est retourné et l'archive avec les documents transformés est contenue dans le corps de la réponse.

Au cas où le job ne serait pas terminé, un statut HTTP 202 est retourné.

Au cas où l'UUID fait référence à un job n'existant pas sur ce serveur, un statut HTTP 404 est retourné.

En fonction de la configuration du serveur, la récupération d'un job terminé peut nécessiter une authentification. Dans ce cas, l'entête HTTP Authorization doit être renseigné sous la forme suivante:

Authorization: [UUID de l'application] [signature HMAC de l'UUID du job]

Voir le source du client Python pour un exemple de signature HMAC.

Notification

Dans le cas où une URL a été fournie dans la clef notify_hook du fichier job.json, le serveur effectue une requête POST sur cette URL avec l'UUID du job en corps de requête.

Serveur de référence

Un serveur de référence est implémenté en Python. Deux composants sont fournis:

  • un serveur HTTP exposant l'interface HTTP du service
  • un pool de workers effectivement chargés des transformations

Pré-requis

  • Python >= 3.10 et < 4 (Python 3.11 utilisé par défaut dans ce dépôt)
  • uv

Installation et démarrage du service

Un Makefile documente les tâches disponibles avec make ou make help. uv doit être installé au préalable. Les principales commandes sont:

make setup         # environnement de développement
make setup-prod    # dépendances du service uniquement
make build         # distributions source et wheel dans dist/
make publish       # construction et publication sur PyPI
make test          # tests avec un serveur temporaire isolé
make check         # vérification du verrou, du formatage et tests
make deps-check    # recherche de vulnérabilités dans les dépendances verrouillées
make run           # service HTTP et workers

make format applique le formateur Ruff et make format-check vérifie le formatage. make lock actualise uv.lock après modification des dépendances. make deps-check nécessite OSV-Scanner v2 installé dans le PATH et un accès réseau à la base OSV. Cette commande vérifie uv.lock, puis scanne ses dépendances, y compris celles de développement. Elle échoue si le scanner signale des vulnérabilités ou rencontre une erreur. Les variables UV, OSV_SCANNER, TEST_ARGS et CIRCE_ARGS sont configurables, par exemple:

make test TEST_ARGS="-v -k polling"
make serve CIRCE_ARGS="--port 8080 --workers 1"
make deps-check OSV_SCANNER=/chemin/vers/osv-scanner

Les commandes uv équivalentes sont détaillées ci-dessous.

Depuis la racine du dépôt, installation de Circe et de ses dépendances de développement:

uv sync --locked

uv crée automatiquement l'environnement virtuel .venv et utilise les versions de uv.lock. Pour installer uniquement les dépendances nécessaires au service:

uv sync --locked --no-dev

Démarrage du service HTTP:

uv run --locked circe serve

Démarrage des workers:

uv run --locked circe start-workers

Démarrage simultané du service HTTP et des workers:

uv run --locked circe run

En production, ajouter --no-dev à uv run pour conserver un environnement sans dépendances de développement.

Pour ajouter une dépendance, utiliser uv add nom-du-paquet (ou uv add --dev nom-du-paquet pour le développement), puis versionner pyproject.toml et uv.lock ensemble. Pour construire les distributions source et wheel dans dist/:

uv build --clear

La construction vide d'abord dist/ pour supprimer les anciennes distributions. Pour construire puis publier les distributions sur PyPI, fournir un jeton API PyPI via la variable d'environnement UV_PUBLISH_TOKEN, puis lancer:

make publish

Variables d'environnement et configuration par défaut:

  • CIRCE_HOST (127.0.0.1)
  • CIRCE_PORT (8000)
  • CIRCE_DEBUG (0)
  • CIRCE_WORKERS (number of CPUs)
  • CIRCE_WORKING_DIR ($HOME/.circe/)
  • CIRCE_ENABLE_WEB_UI (0)
  • CIRCE_WEB_UI_CRYPT_KEY ("you should really change this")
  • CIRCE_WEB_UI_REMOVE_USER_FILES_DELAY (7200)
  • CIRCE_WEB_UI_SECURE_COOKIE (1)
  • CIRCE_USE_AUTH (1)
  • CIRCE_TRANSFORMATIONS_MODULE (None)

Vous pouvez renseigner ces variables à différents endroits, en fonction de vos besoins:

  • dans le fichier lu au démarrage de votre shell (~/.zshrc, ~/.bashrc, etc)
  • dans un fichier .env dans votre répertoire de travail (celui où vous vos trouvez quand vous lancez circe)
  • directement avant la commande circe, sur la même ligne

Exemple type d'un fichier .env:

CIRCE_TRANSFORMATIONS_MODULE=mon_module_de_transfos
CIRCE_ENABLE_WEB_UI=1
CIRCE_WEB_UI_CRYPT_KEY="53CreT"
CIRCE_USE_AUTH=0

Utilisation en ligne de commande

Un certain nombre de commande sont disponibles dans circe. Pour les afficher:

uv run --locked circe --help
usage: circe [-h]
             {serve,start-workers,make-api-access,remove-api-access,list-api-access,list-transformations,run}
             ...

positional arguments:
  {serve,start-workers,make-api-access,remove-api-access,list-api-access,list-transformations,run}
    serve               Start Circe HTTP server
    start-workers       Start job workers
    make-api-access     Create new app uuid / secret couple for api access.
    remove-api-access   Remove access to the API
    list-api-access     List all access tokens to the API
    list-transformations
    run                 Start both HTTP server and job workers

optional arguments:
  -h, --help            show this help message and exit

Il est possible d'obtenir de l'aide sur chaque commande:

uv run --locked circe serve --help
usage: circe serve [-h] [--host HOST] [-p PORT] [-w WORKERS] [-d] [-a]

Start Circe HTTP server

optional arguments:
  -h, --help            show this help message and exit
  --host HOST           '127.0.0.1'
  -p PORT, --port PORT  8000
  -w WORKERS, --workers WORKERS
                        1
  -d, --debug           False
  -a, --access-log      False

Authentification

Lorsque la variable d'environnement CIRCE_USE_AUTH est à "1", le serveur attend une authentification sous la forme d'un hash HMAC du corps de la requête avec une clef partagée entre le serveur est le client. Ce hash est ajouté aux entêtes de la requête avec un identifiant propre au client de la façon suivante:

Authorization: [identifiant du client] [hash HMAC du corps de la requête]

Pour plus de détails sur le hash HMAC, voir les différentes implémentations dans les librairies clients listées en bas de ce README.

Toute la gestion des accès à l'API Circe se fait en ligne de commande.

Pour la création de l'accès:

uv run --locked circe make-api-access -t MonClientDeTest
Access granted to MonClientDeTest
uuid    : 33f3f6c5-3bbc-4eeb-b661-e4579b2d9671
secret: &#H-9csMX|0):'-eUP6'u,I6=5X}U|z/

Le client se voit attribué un UUID ainsi qu'une clef qu'il utilisera pour le hâchage.

Pour la suppression de l'accès:

uv run --locked circe remove-api-access 33f3f6c5-3bbc-4eeb-b661-e4579b2d9671

Pour lister tous les accès:

uv run --locked circe list-api-access
7afa5930a3c549b9a7003c0f98b55e73 : rcY<S<"\oHvjUJdf'w"J:YKE\?AKiG"~  [test client]
3a5eb39cadb04c8e9b30ee167c2e4cb5 : ~1!|J;yxGkW)?Z]hQ\v+Rn*52?`y(_:z  [test client]
a9e836331612498cb1681bc953132d82 : EQbQ&>)BxC|J/5Gz?4$BNr)~&\|k89`I  [test client]
1f902a30f124450bbe267b56026d347f : L$xc8cwFiczK4z7n%.{eSg^y0xFrzBtX  [test client]

Ajouter des transformations

La variable d'environnement CIRCE_TRANSFORMATIONS_MODULE contient le nom du module Python contenant les transformations que vous souhaitez rendre disponible dans le service.

Une transformation est un Python callable (fonction ou classe) prenant en argument le dossier de travail du job, une instance de logging.Logger ainsi qu'un dictionnaire d'options (facultatif). Exemple minimal d'une transformation:

def ne_fait_rien(working_dir: str, logger: logging.Logger, options: dict = None):
    pass  # ajouter ici le code transformant les documents

L'instance de logging.Logger peut prendre ee paramêtre une chaîne ou un dictionnaire:

logger.info('message de log")
logger.info({"message": "message de log", "autre info utile": 42}

Les transformations peuvent fournir une description de leur fonctionnement ainsi:

ne_fait_rien.description = {
	"label": "Ne fait rien",
	"help": "Ne fait rien absolument rien. Utile pour tester l'API.",
	"options": [],  # aucune option pour cette transformation
}

Ces descriptions sont utiles pour les clients.

Des exemples de transformations sont disponibles dans ce dépôt: https://git.unicaen.fr/certic/circe-transformations

Clients de référence

Une librairie cliente de référence en python est proposée.

Installation:

uv add circe-client-CERTIC

Utilisation:

from circe_client import Client

# Les paramètres peuvent être ignorés si les variables
# d'environnement CIRCE_ENDPOINT, CIRCE_SECRET et CIRCE_APP_UUID
# existent.    
client = Client(
    api_endpoint="http://host.tld/,
    secret_key="notsosecret",
    application_uuid="786d1b69a6034eb89178fed2a195a1ed",
)

if "html2pdf" in client.available_transformations():
    job = client.new_job()
    job.add_file("index.html")
    # on peut adjoindre tout fichier utile à la transformation
    job.add_file("style.css")
    job.add_transformation("html2pdf")
    # en option, une URL qui recevra une notification en POST
    # à la fin du job:
    # job.set_notify_hook("https://acme.tld/notify-me/")

    # wait=True pour un appel synchrone à l'API,
    # à privilégier pour les jobs courts et/ou les
    # transformations rapides:
    client.send(job, wait=True)
    
    # pour un appel asynchrone, retournant un UUID de job,
    # à privilégier pour les jobs longs (transformations lentes
    # et/ou nombreux fichiers):
    #
    # client.send(job)
    # print(job.uuid)
    # 
    # On peut ensuite tenter une récupération du job avec
    # un timeout:
    #
    # client.poll(job, timeout=60)

    # liste les fichiers disponible dans l'archive de résultat
    # sous la forme d'un tuple (nom de fichier, pointeur vers le fichier)
    for file_name, file_handle in job.result.files:
        print(file_name)

Une librairie cliente équivalente en Java est disponible ainsi qu'une librairie minimale en PHP et un outil en ligne de commande implémenté en Go.

Tests

Un ensemble de tests exécutables par Pytest sont disponibles dans le fichier test.py.

Pour les lancer sans configuration supplémentaire:

make test

Cette commande démarre un serveur local sur un port libre avec la transformation sleep_well, exécute les tests dans un dossier temporaire avec une configuration et une base d'authentification isolées, puis arrête le serveur et supprime le dossier. Les transformations sont exécutées en mode immédiat, sans workers séparés.

Pour tester un serveur déjà démarré, utiliser make test-live. Ces tests nécessitent un service proposant la transformation sleep_well, ainsi que la même configuration (CIRCE_WORKING_DIR, hôte et port) que le processus de test. Après uv sync --locked, les lancer explicitement avec:

uv run --locked pytest test.py

Mise en production

Une façon simple de faire fonctionner Circe en production sur Linux est de maintenir le service via systemd et de le mettre en reverse proxy derrière un serveur web.

Pour le service sous systemd, ajouter ceci dans un fichier /etc/systemd/system/circe.service, en prenant soin de changer les chemins en fonction de votre installation, ainsi que votre utilisateur/groupe:

[Unit]
Description=Circe Service
 
[Service]
Type=simple
 
# ici on a créé un utilisateur spécifique pour le service
User=circe
Group=circe
UMask=007
 
# Dépôt installé avec uv sync --locked --no-dev
WorkingDirectory=/home/circe/circe-server
ExecStart=/home/circe/circe-server/.venv/bin/circe run
Environment="PATH=/home/circe/circe-server/.venv/bin:/usr/local/bin:/usr/bin:/bin"
# Vous pouvez changer ici toutes les variables d'environnement propres à la configuration de Circe
Environment="CIRCE_WEB_UI_CRYPT_KEY=qpoiurfIPUBeriIPU"
Environment="CIRCE_TRANSFORMATIONS_MODULE=your_transformations"
Environment="CIRCE_ENABLE_WEB_UI=0"
Restart=always 
RestartSec=0
 
# Configures the time to wait before service is stopped forcefully.
TimeoutStopSec=30

[Install]
WantedBy=multi-user.target

Puis, dans votre shell avec les droits root:

systemctl daemon-reload
systemctl enable circe
systemctl start circe

Vous pouvez vérifier que le service est bien démarré avec la commande suivante:

systemctl status circe

Le serveur devrait écouter en locahost (127.0.0.1) sur le port 8000 si vous n'avez pas modifié sa configuration. Vous pouvez maintenant placer ce service derrière le serveur web de votre choix.

Pour Caddy, dans votre /etc/caddy/CaddyFile:

circe.yourhost.com {
	reverse_proxy 127.0.0.1:8000
	header -Server
}

Pour Apache, dans la configuration de votre vhost:

<Location />
	ProxyPass "http://127.0.0.1:8000/"
	ProxyPassReverse "http://127.0.0.1:8000/"
</Location>

Référez-vous à la documentation de votre serveur web pour plus de détail.

Metadata

Release files for circe-CERTIC 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for circe-CERTIC 0.1.0
File Size Uploaded Provenance Actions
circe_certic-0.1.0.tar.gz 88.0 kB Without provenance Details for circe_certic-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for circe-CERTIC 0.1.0
File Interpreter ABI Platform Actions
circe_certic-0.1.0-py3-none-any.whl Python 3 none any Details for circe_certic-0.1.0-py3-none-any.whl

Total release size: 170.5 kB

Release files / circe_certic-0.1.0.tar.gz

Download URL circe_certic-0.1.0.tar.gz
Size 88.0 kB
Tags Source
SHA-256 checksum
How to use checksums
8b5290e13d8e8bf515af4a6d600248b4b2e157eae4c9ee8d2e5df7de27e896ab
BLAKE2b-256 checksum
How to use checksums
ef179dcdd7ba1e59aaae02293aa8642c0d3bdaf3828f42308230838ebf2ec2b4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / circe_certic-0.1.0-py3-none-any.whl

Download URL circe_certic-0.1.0-py3-none-any.whl
Size 82.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6bafc9e41269210ff612706cd8cf883d92abc46573c22bc67c7f9bba4e1e510e
BLAKE2b-256 checksum
How to use checksums
0d6cbc20b1acd431f04aee1349aa71b6f856d9536a5fa233a68018a6c810227b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for circe-CERTIC 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page