Documentazione

Assegna ai server e ai lavori CI il proprio accesso

Un client di servizio è un server, script o job CI che lavora su un progetto con proprie credenziali. Accede con una chiave privata o un segreto, riceve un token di accesso di 15 minuti e non necessita mai dell’approvazione di una persona per ogni esecuzione.

Cos’è un cliente di servizio

  • Proprietà del progetto. Il progetto mantiene il cliente quando la persona o l’agente che l’ha creato se ne va. Le sue richieste sono fatturate come richieste agente, contro gli stessi richieste incluse.
  • Solo lettura, Proponi o Gestisci. Impostato alla creazione, come un agente connesso. Solo lettura elenca e legge; Proponi carica anche contenuti multimediali e propone modifiche a playlist e schermo per la revisione di una persona; Gestisci modifica, pubblica e spende. Quando il progetto richiede revisione per modifiche degli agenti, un client lavora al massimo in Proponi. Vedi Solo lettura, Proponi e Gestisci.
  • Capacità. Qualsiasi di screens, content, playlists, advertising, reports e project, entro quelle del creatore.
  • Limitato. Un client di servizio non crea né gestisce client di servizio, persone, inviti o accessi agenti.
  • Due modi per accedere. Una chiave pubblica registrata (private_key_jwt, il metodo consigliato) o un segreto (client_secret_basic). Un client può avere fino a due di ciascuno, così puoi ruotare senza interruzioni.

Un’organizzazione contiene fino a 50 client servizio live in tutti i suoi progetti. Gli id client iniziano con scl_ e i segreti con sr_cs_.

Crea una chiave

Genera una coppia di chiavi Ed25519 sulla macchina che esegue il client. La chiave privata non lascia mai la macchina; screenRIG conserva solo la metà pubblica.

openssl genpkey -algorithm ed25519 -out screenrig-client.pem
chmod 600 screenrig-client.pem

Funzionano anche chiavi EC P-256 e RSA di almeno 2048 bit.

Crea il client

Nel dashboard. Apri Accesso e scegli Nuovo client di servizio. Dagli un nome, scegli Solo lettura, Proponi o Gestisci, seleziona le sue capacità e incolla la chiave pubblica come JWK, oppure scegli Genera un segreto client. Stampa il JWK pubblico della tua chiave con Node:

node -e 'const c=require("node:crypto");const k=c.createPublicKey(require("node:fs").readFileSync("screenrig-client.pem"));console.log(JSON.stringify(k.export({format:"jwk"})))'

Con la CLI. L’agente deve avere accesso Gestisci per la propria identità con l’ambito identity, una propria autorizzazione Gestisci con la capacità project sul progetto di destinazione e una persona responsabile verificata che amministri l’organizzazione di quel progetto.

Per un agente appena registrato, la persona invitata deve prima accettare l’invito di registrazione. Il solo accesso Gestisci al progetto non autorizza modifiche alle credenziali.

--key-file accetta un file JWK o PEM e invia solo la sua parte pubblica:

screenrig service-client create --name "Nightly report" --access read \
  --capability reports --key-file screenrig-client.pem

Per usare invece un segreto, aggiungi --secret-file PATH. La CLI scrive il segreto in quel nuovo file con modalità 0600; viene mostrato una volta e mai più.

screenrig service-client show scl_ID elenca gli ID chiave del client (kid) e gli ID segreti. Una chiave senza il proprio kid è nota tramite il suo thumbprint RFC 7638.

Client uploader

L’agente di un’agenzia riceve un client limitato a playlist e cartelle media scelte: aggiungi --uploader --folder-id mfd_FOLDER[,mfd_FOLDER] --playlist-id pl_PLAYLIST[,pl_PLAYLIST] a service-client create. Un client uploader ha solo le capacità content e playlists e agisce su Proponi a meno che --access non dica altro. Con il suo token carica nelle sue cartelle (screenrig media upload FILE --folder mfd_FOLDER) e aggiunge, sostituisce e rimuove pagine delle sue playlist (screenrig playlist page add|replace|remove); tutto il resto risponde outside_upload_scope, il cui upload_scope elenca ciò che può usare. Il proprietario del progetto modifica in seguito le cartelle e le playlist di un client; la modifica si applica al token già emesso. Vedi Uploader.

Ottieni un token di accesso

Chiedi all’endpoint token un token con la concessione client_credentials. La risposta contiene access_token, token_type: Bearer, expires_in (900 secondi) e scope. Non esiste un token di aggiornamento: chiedi di nuovo alla scadenza.

Con un segreto, autentica con HTTP Basic. Un segreto nel corpo della richiesta è rifiutato:

curl -s https://api.screenrig.ai/oauth/token \
  -u "$SCREENRIG_CLIENT_ID:$SCREENRIG_CLIENT_SECRET" \
  -d grant_type=client_credentials

Con una chiave, invia un’asserzione client firmata invece di un segreto:

curl -s https://api.screenrig.ai/oauth/token \
  -d grant_type=client_credentials \
  -d client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
  -d client_assertion="$ASSERTION"

L’asserzione è un JWT firmato con la tua chiave privata:

CampoValore
Intestazione algEdDSA, ES256 o RS256, corrispondenti alla chiave
Intestazione kidL’ID della chiave registrata
iss e subL’id client
audhttps://api.screenrig.ai o https://api.screenrig.ai/oauth/token, come una stringa
iat, expAl massimo 5 minuti di distanza
jtiUn nuovo valore casuale per ogni asserzione; una ripetizione è rifiutata

Le intestazioni jku, x5u, jwk e x5c sono rifiutate.

Chiedi meno

Due parametri opzionali restringono un token:

  • scope: access:read o access:propose assegna al token un livello inferiore rispetto al client, e i nomi delle capacità mantengono solo quelle capacità, per esempio scope=access:read reports. Un ambito oltre il client è invalid_scope.
  • resource: https://api.screenrig.ai/api, predefinito, per l’API REST, o https://api.screenrig.ai/mcp per il server remoto.

Chiamare l’API

Invia il token come bearer. Agisce sul progetto del client:

curl -s https://api.screenrig.ai/api/screens \
  -H "Authorization: Bearer $TOKEN"

Node: un token memorizzato nella cache con una chiave

Questo helper firma l’asserzione con la crittografia integrata di Node, mantiene il token in memoria e ne richiede uno nuovo un minuto prima della scadenza. Imposta CLIENT_KEY_ID alla kid della chiave da service-client show:

import { createPrivateKey, randomUUID, sign } from "node:crypto";
import { readFileSync } from "node:fs";

const ISSUER = "https://api.screenrig.ai";
const { SCREENRIG_CLIENT_ID: clientId, SCREENRIG_CLIENT_KEY_FILE: keyFile, CLIENT_KEY_ID: kid } = process.env;
const key = createPrivateKey(readFileSync(keyFile));
const part = (value) => Buffer.from(JSON.stringify(value)).toString("base64url");
let cached;

function assertion() {
  const now = Math.floor(Date.now() / 1000);
  const input = `${part({ alg: "EdDSA", typ: "JWT", kid })}.${part({ iss: clientId, sub: clientId, aud: ISSUER, iat: now, exp: now + 60, jti: randomUUID() })}`;
  return `${input}.${sign(null, Buffer.from(input), key).toString("base64url")}`;
}

export async function accessToken() {
  if (cached && cached.expiresAt - 60_000 > Date.now()) return cached.token;
  const response = await fetch(`${ISSUER}/oauth/token`, {
    method: "POST",
    body: new URLSearchParams({
      grant_type: "client_credentials",
      client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
      client_assertion: assertion(),
    }),
  });
  const body = await response.json();
  if (!response.ok) throw new Error(`screenRIG token request refused: ${body.error}: ${body.error_description ?? ""}`);
  cached = { token: body.access_token, expiresAt: Date.now() + body.expires_in * 1000 };
  return cached.token;
}

Python: un token memorizzato nella cache con un segreto

import base64, json, os, time, urllib.parse, urllib.request

ISSUER = "https://api.screenrig.ai"
_cached = {"token": None, "expires_at": 0.0}


def access_token() -> str:
    if _cached["token"] and _cached["expires_at"] - 60 > time.time():
        return _cached["token"]
    pair = f'{os.environ["SCREENRIG_CLIENT_ID"]}:{os.environ["SCREENRIG_CLIENT_SECRET"]}'
    request = urllib.request.Request(
        f"{ISSUER}/oauth/token",
        data=urllib.parse.urlencode({"grant_type": "client_credentials"}).encode(),
        headers={"Authorization": "Basic " + base64.b64encode(pair.encode()).decode()},
    )
    with urllib.request.urlopen(request, timeout=10) as response:
        body = json.load(response)
    _cached.update(token=body["access_token"], expires_at=time.time() + body["expires_in"])
    return body["access_token"]

Esegui la CLI come service client

Accedi con la CLI come client una sola volta, in una configurazione tutta sua. La configurazione registra il client e il file che contiene la sua chiave privata (consigliata) o il suo segreto, mai la chiave o il segreto stessi:

screenrig --config /run/screenrig/ci.json login --client-id scl_ID \
  --client-key-file /run/secrets/screenrig-client.pem
screenrig --config /run/screenrig/ci.json screen list

Usa --client-secret-file FILE al posto di --client-key-file per un segreto. Ogni comando con quella configurazione viene eseguito come il client, senza passaggio di accesso: legge ogni volta il file della chiave o del segreto (quindi un file ruotato non richiede un nuovo accesso), mantiene il token in memoria e non scrive nient’altro nella configurazione. screenrig logout rimuove il client da essa. Ogni comando richiede il proprio token, quindi un lavoro che esegue più di qualche centinaio di comandi all’ora è meglio servito da un token riutilizzato tramite API.

Ruota e revoca

ADashboard (Accesso, il cliente)CLI
Aggiungi una seconda chiaveAggiungi chiavescreenrig service-client add-key scl_ID --key-file new.pem
Rimuovi una chiaveRimuovi chiavescreenrig service-client remove-key scl_ID --kid KID
Aggiungi un secondo segretoGenera segretoscreenrig service-client add-secret scl_ID --secret-file new-secret
Rimuovi un segretoRimuovi segretoscreenrig service-client remove-secret scl_ID --secret-id css_ID
Modifica accesso o capacitàAccedi, poi Salva accessoUsa il dashboard
Termina il clientRevocascreenrig service-client revoke scl_ID --yes

Per ruotare senza inattività, aggiungi la nuova chiave o segreto, distribuiscilo, poi rimuovi il vecchio. I token emessi con una chiave o segreto rimosso smettono di funzionare in pochi secondi, e un client mantiene almeno una chiave o segreto.

Limitare l’accesso o le capacità di un cliente invalida i suoi token attuali; il suo prossimo token avrà il nuovo accesso. La revoca è permanente: chiavi e segreti vengono eliminati, i token del cliente smettono di funzionare entro pochi secondi e il record rimane nella pagina Accesso per audit. Eliminare il progetto revoca i suoi clienti.

Un client ottiene token solo mentre il suo progetto è attivo. Un progetto annullato o eliminato non ne emette. Un progetto a credito zero emette comunque token, e le sue richieste a pagamento rispondono 402 payment_required, come fa un agente.

Errori e limiti

RispostaCausaCorreggi
401 invalid_clientSegreto errato o rimosso, asserzione errata o ripetuta, kid sconosciuto, client revocato o progetto annullato o eliminatoControlla le credenziali rispetto a service-client show; invia un nuovo jti ogni volta
400 invalid_scopeUn ambito oltre l’accesso o le capacità del clientChiedi meno o cambia il client
400 invalid_targetUn resource diverso dai due elencatiUsa il server predefinito o quello remoto
429 rate_limitedPiù di 600 token per client, o 1.200 richieste token per indirizzo, in un’oraRiutilizza ogni token fino alla sua scadenza
503 temporarily_unavailableRiprova tra pocoRitenta con backoff
401 da /apiIl token è scaduto o è stato revocatoChiedi un nuovo token una volta, poi interrompi
403 forbidden durante la gestione dei clientManca l’autorità sull’identità, un’appartenenza attuale al progetto con la capacità project o il ruolo di amministratore dell’organizzazione della persona responsabileChiedi alla persona invitata di accettare la registrazione; verifica l’accesso Gestisci all’identità, l’autorizzazione Gestisci dell’agente sul progetto e il ruolo di amministratore dell’organizzazione della persona responsabile
403 insufficient_access da /apiIl livello di accesso del token non può effettuare quella modifica, oppure il progetto richiede revisione per modifiche agentUsa un client Manage; mentre il progetto richiede revisione, modifiche a playlist e schermo vanno a Revisioni
402 payment_required da /apiIl progetto non ha credito per una richiesta a pagamentoAggiungi credito al progetto