Use it: examples

Everything here talks to the same server with the same formats, so a file sent one way opens every other way, including on the open page.

From a page

<script type="module">
  import * as send from 'https://api.send.sgit.ai/v0.1.1/send/send-api.js'
  const api = new send.SendTransfers({ endpoint: 'https://send.sgraph.ai', accessToken })
  const r   = await api.send({ bytes, filename: 'report.pdf', expiresAt: Date.now() + 7 * 86400e3 })
  const url = send.link.transferLink({ openUrl: 'https://send.sgit.ai/open/', transferId: r.transferId, key: r.keyString })
  qr.innerHTML = send.qr.toSvg(url)
</script>

Full walk-through, including a friendly token and a vault: the quick start.

From a shell, with curl and openssl

The layout is simple enough to do by hand: envelope, encrypt, three calls.

TOKEN=...; HOST=https://send.sgraph.ai; FILE=report.pdf
KEY=$(openssl rand -hex 32); IV=$(openssl rand -hex 12)
META=$(printf '{"filename":"%s"}' "$FILE"); LEN=$(printf '%s' "$META" | wc -c)
{ printf 'SGMETA\0'; printf "$(printf '\\x%02x\\x%02x\\x%02x\\x%02x' $((LEN>>24&255)) $((LEN>>16&255)) $((LEN>>8&255)) $((LEN&255)))"; printf '%s' "$META"; cat "$FILE"; } > plain.bin
# AES-256-GCM: openssl's CLI cannot emit the GCM tag; use the Python example below for GCM, or sgit's transfer command when it ships.

A GCM tag is required, and OpenSSL's command line does not produce one, so the honest shell example stops here: the three HTTP calls are below in Python, and they are the same from curl.

ID=$(curl -s -H "x-sgraph-access-token: $TOKEN" -H 'content-type: application/json' -d '{"file_size_bytes":1234,"content_type_hint":"application/octet-stream"}' $HOST/api/transfers/create | python3 -c 'import json,sys;print(json.load(sys.stdin)["transfer_id"])')
curl -s -H "x-sgraph-access-token: $TOKEN" -H 'content-type: application/octet-stream' --data-binary @cipher.bin $HOST/api/transfers/upload/$ID
curl -s -X POST -H "x-sgraph-access-token: $TOKEN" $HOST/api/transfers/complete/$ID
echo "https://send.sgit.ai/open/#$ID/$KEY_BASE64URL"

From Python, for an agent

An agent that finishes a job and hands the result over once. Standard library plus cryptography; no account for the recipient.

import base64, json, os, struct, urllib.request
from cryptography.hazmat.primitives.ciphers.aead import AESGCM

HOST, TOKEN = 'https://send.sgraph.ai', os.environ['SGRAPH_ACCESS_TOKEN']   # the token: an env var, a header, never a URL

def send_file(path, expires_ms=0, max_downloads=0):
    data = open(path, 'rb').read()
    meta = json.dumps({'filename': os.path.basename(path)}).encode()
    plain = b'SGMETA\0' + struct.pack('>I', len(meta)) + meta + data          # the name inside the ciphertext
    key, iv = AESGCM.generate_key(256), os.urandom(12)
    cipher = iv + AESGCM(key).encrypt(iv, plain, None)                        # IV || ciphertext || tag
    def call(method, p, body=None, ctype='application/json'):
        req = urllib.request.Request(HOST + p, data=body, method=method, headers={'x-sgraph-access-token': TOKEN, 'content-type': ctype})
        with urllib.request.urlopen(req) as r: return r.read()
    tid = json.loads(call('POST', '/api/transfers/create', json.dumps({'file_size_bytes': len(data), 'content_type_hint': 'application/octet-stream', 'max_downloads': max_downloads, 'expires_at': expires_ms}).encode()))['transfer_id']
    call('POST', f'/api/transfers/upload/{tid}', cipher, 'application/octet-stream')
    call('POST', f'/api/transfers/complete/{tid}')
    k = base64.urlsafe_b64encode(key).decode().rstrip('=')
    return f'https://send.sgit.ai/open/#{tid}/{k}'                             # the key: in the fragment, never logged

def receive(link):
    frag = link.split('#', 1)[1].split('|')[0]; tid, k = frag.split('/', 1)
    key = base64.urlsafe_b64decode(k + '=' * (-len(k) % 4))
    with urllib.request.urlopen(f'{HOST}/api/transfers/download/{tid}') as r: cipher = r.read()   # public: the key is the gate
    plain = AESGCM(key).decrypt(cipher[:12], cipher[12:], None)
    assert plain[:7] == b'SGMETA\0'; n = struct.unpack('>I', plain[7:11])[0]
    return json.loads(plain[11:11 + n])['filename'], plain[11 + n:]

For an agent-facing link prefer max_downloads=0 with a short expires_ms: a retry is not a reader, and a cap burns the allowance. For agents has the rest of that guidance.

On a laptop, with the server in memory

git clone https://github.com/SGit-AI/SGit-AI__Website__Send__API
node SGit-AI__Website__Send__API/releases/v0.1.1/tests/server.mjs 8787 local-dev-token

Then in Settings choose "a server on this machine" with the token local-dev-token. Every feature works, nothing leaves the machine, and it forgets everything when it stops. It is the same server the API's 43 tests run against.