Misi.Link

API dokumentatsioon

Saatke faile oma tööriistadest (skriptid, programmid, serverid): looge ülekanne, laadige failid üles osade kaupa, saage link.

Võti on saadaval Pro- ja Äri-paketis, jaotises Minu konto › API. Hinnad

Autentimine

Looge võti jaotises Minu konto › API ja saatke see iga päringu Authorization-päises. Hoidke see salajas: see annab juurdepääsu teie ülekannetele. Kustutatud võti lakkab kohe töötamast.

Authorization: Bearer mlk_…

Kui teie tööriist või majutus ei edasta päist Authorization, saatke sama võti päises X-API-Key:

X-API-Key: mlk_…

120 päringut minutis võtme kohta. Suurus, failide arv ja saadavusaeg: vastavalt teie paketile.

Failide saatmine kolmes etapis

1. Looge ülekanne

Esitage failide loend (nimi ja suurus baitides). Väljaga „recipients” saab iga saaja e-kirja; ilma saajateta saate jagatava lingi. Valikuline: „message”, „password”, „expire_days”, „delete_after_download”.

curl -X POST https://misi.link/api/v1/transfers \
  -H "Authorization: Bearer $MISILINK_KEY" -H "Content-Type: application/json" \
  -d '{"files": [{"name": "film.mov", "size": 5368709120}],
       "recipients": ["client@example.com"], "message": "…", "expire_days": 14}'
{"ok": true, "id": "aB3dE5fG", "token": "…", "url": "https://misi.link/aB3dE5fG",
 "files": [{"id": 101, "name": "film.mov", "size": 5368709120}], "chunk_size": 8388608}

2. Laadige failid üles

Saatke iga fail kuni „chunk_size” baidi suuruste osadena järjest: „offset” on selle faili juba vastu võetud baitide arv. Pärast katkestust näitab „status”, kust jätkata (vastus 409: jätkake kohast „received”). Kui teie majutus või tulemüür filtreerib aadresse, saatke asukoht aadressi asemel päises X-Upload-Offset.

curl -X POST "https://misi.link/api/upload/chunk?id=aB3dE5fG&token=…&file=101&offset=0" \
  -H "Content-Type: application/octet-stream" --data-binary @morceau-1.bin
# {"ok": true, "received": 8388608, "done": false}

curl "https://misi.link/api/upload/status?id=aB3dE5fG&token=…"

3. Lõpetage

Kui kõik failid on kohal, lõpetage: ülekanne avaldatakse ja e-kirjad saadetakse. Link on esimese vastuse „url”. See samm on kohustuslik. Seejärel salvestab server failid: selle ajal on „status” väärtus „finalizing”. Täielikult vastu võetud, kuid lõpetamata ülekanne lõpetatakse automaatselt pärast 30 minutit tegevusetust.

curl -X POST https://misi.link/api/upload/complete -H "Content-Type: application/json" \
  -d '{"id": "aB3dE5fG", "token": "…"}'

Otse salvestusruumi saatmine (soovitatav suurte failide puhul)

Väärtusega „direct”: true saadab teie tarkvara faili otse misi.linki salvestusruumi, serverist mööda: suurim kiirus ja lõpus mõne sekundiga kättesaadav. Üks fail ülekande kohta (mitu: koondage arhiivi). Ilma selle valikuta või kui vastuses on „direct”: null, laadige osad üles „chunk” abil nagu eespool.

  1. Looge ülekanne väärtusega „direct”: true. Vastuses on „direct.part_size” (iga osa suurus, viimane on lühem), „direct.parts” (osade arv) ja „direct.sign”.
  2. Küsige „sign” kaudu allkirjastatud aadressid (tunnus, tõend ja osade numbrid, kuni 100 päringu kohta; kehtivad 6 tundi) ja saatke iga osa tavalise PUT-päringuga selle aadressile, ilma autentimispäiseta. Osi saab saata paralleelselt ja pärast viga uuesti.
  3. Lõpetage „complete” abil. Kui mõni osa puudub, on vastus „incomplete” koos loendiga „missing”: saatke need osad uuesti ja kutsuge „complete” uuesti. Otspunktkrüpteerimine: iga osa on järjestikuste tervete krüpteeritud lõikude jada (vt näidet).
curl -X POST https://misi.link/api/v1/transfers -H "Authorization: Bearer $MISILINK_KEY" -H "Content-Type: application/json" \
  -d '{"files": [{"name": "film.mov", "size": 5368709120}], "direct": true}'
# {…, "direct": {"part_size": 16777216, "parts": 320, "total": 5368709120, "sign": "https://misi.link/api/upload/sign"}}

curl -X POST https://misi.link/api/upload/sign -H "Content-Type: application/json" \
  -d '{"id": "aB3dE5fG", "token": "…", "parts": [1, 2, 3]}'
# {"ok": true, "urls": {"1": "https://…", "2": "https://…", "3": "https://…"}}

curl -X PUT -H "Content-Type: application/octet-stream" --data-binary @part-1.bin "https://…"

curl -X POST https://misi.link/api/upload/complete -H "Content-Type: application/json" -d '{"id": "aB3dE5fG", "token": "…"}'
# {"ok": false, "error": "incomplete", "missing": [2]}

Otspunktkrüpteerimine (valikuline)

Teie tarkvara krüpteerib faili enne saatmist: misi.link võtab vastu ja salvestab ainult loetamatuid andmeid. Võtit ei saadeta kunagi serverisse: see on lingis, pärast märki „#”. Saaja laadib alla ja dekrüpteerib oma brauseris, tavalisel lehel.

  1. Looge juhuslik 16-baidine võti ja looge ülekanne väärtusega „e2e”: true ning ühe failiga (selle krüpteerimata suurus baitides). Mitu faili koondage enne arhiivi.
  2. Krüpteerimisvõti: võtme HKDF-SHA-256 (sool „misilink-e2e-v1”, info = ülekande tunnus, 16 baiti), AES-GCM jaoks.
  3. Jagage fail 4 MiB (4 194 304 baidi) osadeks. Osa nr i: nonce = 4 nullbaiti ja seejärel i 8 baidil (big-endian); seotud andmed = i 8 baidil ja seejärel 1 bait (viimase osa puhul 1, muidu 0). Iga krüpteeritud osa on 16 baiti pikem: laadige need baidid üles „chunk” abil järjest (kokku „e2e.encrypted_size”) ja lõpetage „complete” abil.
  4. Soovitatav: „e2e_verify” = võtme HKDF-SHA-256 (sama sool, info „misilink-verify”, 32 baiti) base64url-kujul. Siis keeldub misi.link vale võtmega allalaadimisest, teadmata kunagi õiget.
  5. Jagatav link: „url” + „#” + võti base64url-kujul (22 märki). Saajatega („recipients”) lisage „e2e_key” (võti): misi.link hoiab seda pitseerituna, et lisada see e-kirjade linkidesse.
curl -X POST https://misi.link/api/v1/transfers \
  -H "Authorization: Bearer $MISILINK_KEY" -H "Content-Type: application/json" \
  -d '{"files": [{"name": "film.mov", "size": 5368709120}], "e2e": true, "e2e_verify": "…"}'
# {"ok": true, "id": "aB3dE5fG", …, "e2e": {"segment": 4194304, "tag": 16, "encrypted_size": 5368729600, "version": 1}}

Täielik näide Pythonis (teek cryptography):

#!/usr/bin/env python3
# misi.link: end-to-end encrypted upload through the API (Python 3.8+, pip install cryptography)
# The file goes straight to the storage (signed URLs); if direct upload is unavailable, through "chunk".
# Usage: MISILINK_KEY=mlk_... python3 misilink_e2e.py file.mov [https://misi.link]
import base64, json, os, sys, urllib.request
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives.hashes import SHA256
from cryptography.hazmat.primitives.kdf.hkdf import HKDF

SEG, SALT = 4 * 1024 * 1024, b'misilink-e2e-v1'
b64 = lambda b: base64.urlsafe_b64encode(b).rstrip(b'=').decode()
hkdf = lambda ikm, info, n: HKDF(SHA256(), n, SALT, info).derive(ikm)


def call(url, body=None, raw=None, headers=None, method=None, auth=True):
    h = dict(headers or {})
    if auth:
        h['Authorization'] = 'Bearer ' + os.environ['MISILINK_KEY']
    data = raw if raw is not None else (json.dumps(body).encode() if body is not None else None)
    if data is not None:
        h.setdefault('Content-Type', 'application/octet-stream' if raw is not None else 'application/json')
    req = urllib.request.Request(url, data=data, headers=h, method=method or ('POST' if data is not None else 'GET'))
    with urllib.request.urlopen(req, timeout=600) as r:
        body = r.read()
        return json.loads(body) if body[:1] == b'{' else {}


def segments(path, key, size):
    n = max(1, -(-size // SEG))
    with open(path, 'rb') as f:
        for i in range(n):
            last = i == n - 1
            nonce = bytes(4) + i.to_bytes(8, 'big')
            aad = i.to_bytes(8, 'big') + (b'\x01' if last else b'\x00')
            yield key.encrypt(nonce, f.read(SEG), aad)        # SEG bytes in, SEG + 16 bytes out


def send(path, base='https://misi.link'):
    size = os.path.getsize(path)
    secret = os.urandom(16)                                   # the key: never sent to the server
    verify = b64(hkdf(secret, b'misilink-verify', 32))       # lets the server recognise the right key
    t = call(base + '/api/v1/transfers', {'files': [{'name': os.path.basename(path), 'size': size}],
                                         'e2e': True, 'e2e_verify': verify, 'direct': True})
    key = AESGCM(hkdf(secret, t['id'].encode(), 16))
    auth = {'id': t['id'], 'token': t['token']}
    d = t.get('direct')
    if d:                                                     # straight to the storage: parts of d['part_size'] bytes
        part, n, urls = b'', 0, {}
        def put(n, data):
            if str(n) not in urls:
                urls.update(call(d['sign'], {**auth, 'parts': list(range(n, min(n + 100, d['parts'] + 1)))})['urls'])
            call(urls[str(n)], raw=data, method='PUT', auth=False)
        for seg in segments(path, key, size):
            part += seg
            if len(part) == d['part_size']:
                n += 1; put(n, part); part = b''
        if part:
            n += 1; put(n, part)
    else:                                                     # through the server
        sent = 0
        for seg in segments(path, key, size):
            r = call(t['upload']['chunk'] + '?id=' + t['id'] + '&token=' + t['token'] + '&file=' + str(t['files'][0]['id']),
                     raw=seg, headers={'X-Upload-Offset': str(sent)})
            sent = r['received']
    call(t['upload']['complete'], auth)
    return t['url'] + '#' + b64(secret)                      # the key travels after "#", only in the link


if __name__ == '__main__':
    print(send(sys.argv[1], *(sys.argv[2:3] or [])))

Muud päringud

GET /api/v1/transfers
Teie ülekanded, 50 lehe kohta (?page=2…).
GET /api/v1/transfers/{id}
Üks ülekanne: olek, link, failid, allalaadimiste arv, aegumiskuupäev.
DELETE /api/v1/transfers/{id}
Ülekande kustutamine (link lakkab töötamast).
GET /api/v1/me
Teie konto, pakett ja selle piirangud, osade suurus.

Kuupäevad ISO 8601 vormingus (UTC), suurused baitides.

Vastused ja vead

Kõik vastused on JSON-vormingus väljaga „ok”. Vea korral: „error” (lühikood) ja „message”. HTTP-koodid: 401 võti puudub või on vigane, 403 API-ta pakett, 404 ei leitud, 409 osa vales kohas, 422 päring tagasi lükatud, 429 liiga palju päringuid (oodake minut).