Izstrādātājiem & AI

Vairogs API & MCP

Piekļūstiet sava seifa saturam programmatiski ar zero-knowledge šifrēšanu.

Rakstīšana (Write)

POST / PUT / DELETE

Kā izveidot, atjaunināt un dzēst vienumus ar Tag API v2

Tag API v2 atbalsta pilnu CRUD funkcionalitāti. Visi jaunie vienumi tiek šifrēti klienta pusē pirms nosūtīšanas serverim, nodrošinot zero-knowledge arhitektūru.

Svarīgi

Rakstīšanas operācijām dati jāšifrē pirms nosūtīšanas. Jāizveido gan šifrētie dati, gan key slots galvenajam lietotājam un MCP atslēgai.

Šifrēšanas Process

1

Ģenerē jaunu DEK

32 baitu gadījuma atslēga ar crypto.randomBytes(32)

2

Šifrē datus ar AES-GCM

JSON.stringify(data) → AES-256-GCM ar jaunu DEK un gadījuma IV

3

Izveido Key Slots

Šifrē DEK ar NaCl box gan galvenajam lietotājam (userPublicKey), gan MCP atslēgai

4

Nosūti serverim

POST ar encryptedData, iv, keySlots un metadatiem

typescript
import nacl from 'tweetnacl'
import crypto from 'crypto'

// Utilītas
function generateDEK() {
  return crypto.randomBytes(32)
}

function aesEncrypt(data: any, dek: Buffer) {
  const iv = crypto.randomBytes(12)
  const cipher = crypto.createCipheriv('aes-256-gcm', dek, iv)

  const encrypted = Buffer.concat([
    cipher.update(JSON.stringify(data)),
    cipher.final()
  ])
  const authTag = cipher.getAuthTag()

  return {
    encryptedData: Buffer.concat([encrypted, authTag]).toString('base64'),
    iv: iv.toString('base64')
  }
}

function createKeySlot(dek: Buffer, recipientPublicKey: Uint8Array, senderKeypair: nacl.BoxKeyPair) {
  const nonce = nacl.randomBytes(24)
  const encryptedDEK = nacl.box(
    new Uint8Array(dek),
    nonce,
    recipientPublicKey,
    senderKeypair.secretKey
  )

  return {
    encryptedDEK: Buffer.from(encryptedDEK).toString('base64'),
    nonce: Buffer.from(nonce).toString('base64'),
    senderPublicKey: Buffer.from(senderKeypair.publicKey).toString('base64')
  }
}

// Jauna vienuma izveidošana
async function createItem(
  type: string,
  title: string,
  data: any,
  tagId: string,
  userPublicKey: Uint8Array,
  mcpKeypair: nacl.BoxKeyPair,
  accessKey: string
) {
  // 1. Ģenerē DEK
  const dek = generateDEK()

  // 2. Šifrē datus
  const { encryptedData, iv } = aesEncrypt(data, dek)

  // 3. Izveido key slots
  const mcpKeyPrefix = accessKey.substring(0, 15)
  const keySlots = [
    { keyId: 'main', ...createKeySlot(dek, userPublicKey, mcpKeypair) },
    { keyId: mcpKeyPrefix, ...createKeySlot(dek, mcpKeypair.publicKey, mcpKeypair) }
  ]

  // 4. Nosūta serverim
  const response = await fetch('https://vairogs.lv/api/v2/tag/items', {
    method: 'POST',
    headers: {
      'X-API-Key': accessKey,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      type,
      title,
      encryptedData,
      iv,
      labels: [tagId],
      keySlots
    })
  })

  return response.json()
}
POST/api/v2/tag/items

Izveido jaunu šifrētu vienumu.

Headers

ParametrsTipsApraksts
X-API-Key*stringAccess Key (vgtk_a_xxx)
Content-Type*stringapplication/json

Body Parametri

ParametrsTipsApraksts
type*stringPASSWORD, NOTE, TOTP, IMAGE, DOCUMENT
title*stringVienuma nosaukums (nav šifrēts)
encryptedData*stringBase64 šifrētie dati
iv*stringBase64 inicializācijas vektors
labels*string[]Tag IDs masīvs
keySlots*arrayKey slots masīvs

Key Slot Struktūra

typescript
interface KeySlot {
  keyId: string       // 'main' vai MCP key prefix (pirmie 15 simboli)
  encryptedDEK: string   // Base64 šifrētais DEK
  nonce: string          // Base64 NaCl nonce (24 baiti)
  senderPublicKey: string // Base64 sūtītāja publiskā atslēga
}

Atbilde

json
{
  "success": true,
  "data": {
    "id": "item_newxyz123",
    "type": "PASSWORD",
    "title": "New Login",
    "createdAt": "2024-01-15T12:00:00Z"
  }
}
PUT/api/v2/tag/items/:id

Atjaunina esošu vienumu. Jānosūta pilni jaunie šifrētie dati.

URL Parametri

ParametrsTipsApraksts
id*stringItem ID

Body Parametri

ParametrsTipsApraksts
titlestringJauns nosaukums
encryptedData*stringBase64 šifrētie dati
iv*stringBase64 inicializācijas vektors
keySlots*arrayKey slots masīvs

Atceries

Atjauninot vienumu, vienmēr ģenerē jaunu DEK un jaunu IV. Nekad neizmanto tos pašus kriptogrāfiskos parametrus.

typescript
// Atjaunināt vienumu
async function updateItem(itemId: string, newData: any, ...) {
  // Ģenerē JAUNU DEK un IV
  const dek = generateDEK()
  const { encryptedData, iv } = aesEncrypt(newData, dek)

  // Izveido JAUNUS key slots
  const keySlots = [
    { keyId: 'main', ...createKeySlot(dek, userPublicKey, mcpKeypair) },
    { keyId: mcpKeyPrefix, ...createKeySlot(dek, mcpKeypair.publicKey, mcpKeypair) }
  ]

  const response = await fetch(`https://vairogs.lv/api/v2/tag/items/${itemId}`, {
    method: 'PUT',
    headers: {
      'X-API-Key': accessKey,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      encryptedData,
      iv,
      keySlots
    })
  })

  return response.json()
}
DELETE/api/v2/tag/items/:id

Dzēš vienumu neatgriezeniski.

URL Parametri

ParametrsTipsApraksts
id*stringItem ID

Atbilde

json
{
  "success": true,
  "message": "Item deleted successfully"
}

Brīdinājums

Dzēšana ir neatgriezeniska. Vienums un visi ar to saistītie faili tiks pilnībā dzēsti no sistēmas.

SDK Piemēri

Ar @vairogs/sdk šifrēšana notiek automātiski:

typescript
import { VairogsClient } from '@vairogs/sdk'

const client = new VairogsClient({
  accessKey: 'vgtk_a_xxx',
  decryptionKey: 'vgtk_d_xxx'
})

// Create new password
const newPassword = await client.createItem('PASSWORD', 'GitHub Login', {
  username: 'user@example.com',
  password: 'secret123',
  url: 'https://github.com'
})

// Create note
const newNote = await client.createItem('NOTE', 'Important note', {
  content: 'This is secret text...'
})

// Update item
await client.updateItem('item_abc123', {
  username: 'newuser@example.com',
  password: 'newpassword456'
})

// Delete item
await client.deleteItem('item_abc123')

Kļūdu Apstrāde

400Bad Request

Nepareizi formatēti dati vai trūkst obligāto lauku.

401Unauthorized

Nederīga vai trūkstoša API atslēga.

403Forbidden

Nav atļaujas veikt šo darbību (piemēram, items:write nav iespējots).

404Not Found

Vienums ar norādīto ID neeksistē vai nav pieejams.