---
title: "locker.envelope@1 · OpenVibe.Services"
canonical: "https://openvibe.services/docs/contracts/locker.envelope"
updated: null
---

# locker.envelope@1 · OpenVibe.Services

HTML: [locker.envelope@1 · OpenVibe.Services](https://openvibe.services/docs/contracts/locker.envelope)

# LockerEnvelope `locker.envelope@1`

Generated at 2026-11-13 06:25:48 UTC from [`openvibe-contracts v0.419.0`](https://github.com/OpenVibers/OpenVibe.Contracts/tree/v0.419.0) and [`openvibe-sdk v0.161.0`](https://github.com/OpenVibers/OpenVibe.SDK/tree/v0.161.0).

- Version: 1.0.0
- Owner: network
- Visibility: public
- Status: active
- Compatibility: backward
- Schema: [`https://openvibe.network/contracts/locker/envelope.v1.json`](/docs/contracts/locker.envelope.json)

locker.envelope@1: the header of a Locker envelope. It is the JSON that opens every chunk of the envelope bytes: the suite, the chunk size, the padded size, the counter nonce base and one key slot per way in. The content key is never in the header; it is wrapped inside a slot. Every chunk authenticates the header's core fields (version, suite, chunk\_size, padded\_size, nonce\_base, lock) as associated data, so the framing, the size and the lock kind cannot be changed; the slots sit outside that core on purpose, because adding a member re-wraps the content key without re-encrypting the content, and each slot carries its own AES-GCM tag over the content key with its own parameters bound in.

## Fields

| Field | Type | Required | Description | Constraints |
| --- | --- | --- | --- | --- |
| `version` | const | yes |  | - `= "locker.envelope.v1"` |
| `suite` | const | yes |  | - `= "AES-256-GCM"` |
| `chunk_size` | const | yes | The chunk size in bytes: 1 MiB. Fixed for this version. | - `= 1048576` |
| `padded_size` | integer | yes | The size of the padded plaintext in bytes. The true size lives inside the encrypted content, so the server cannot read it; this is the size it may store and show. The size is padded with Padmé (Nikitin et al., PoPETs 2019): it is the next size on a grid that gets finer as the content grows, at most twelve per cent over the real one, and never below 256 bytes. Every chunk but the last is a whole chunk; the last holds what is left of this size. | - `minimum 256` |
| `nonce_base` | string | yes | Eight random bytes, base64: the first half of every chunk nonce. The second half is the chunk counter, so no nonce repeats inside one envelope. | - `pattern ^[A-Za-z0-9+/]{11}=$` |
| `lock` | enum | yes | Which way in the envelope has. Exactly one slot kind is present, and it matches this. | - `one of "people", "password", "link"` |
| `slots` | object | yes |  | - `no other fields` |
| `slots.people` | [locker.people-slot](/docs/contracts/locker.people-slot) |  |  | - `minItems 1`<br>- `maxItems 100` |
| `slots.password` | [locker.password-slot](/docs/contracts/locker.password-slot) |  |  |  |
| `slots.link` | [locker.link-slot](/docs/contracts/locker.link-slot) |  |  |  |

## Examples

From the contract's own test fixtures: valid ones validate, rejected ones must fail.

 Valid: link

~~~~
{
  "version": "locker.envelope.v1",
  "suite": "AES-256-GCM",
  "chunk_size": 1048576,
  "padded_size": 256,
  "nonce_base": "YmJiYmJiYmI=",
  "lock": "link",
  "slots": {
    "link": {}
  }
}
~~~~

Valid: password

~~~~
{
  "version": "locker.envelope.v1",
  "suite": "AES-256-GCM",
  "chunk_size": 1048576,
  "padded_size": 256,
  "nonce_base": "YmJiYmJiYmI=",
  "lock": "password",
  "slots": {
    "password": {
      "algorithm": "PBKDF2-SHA256/AES-256-GCM",
      "kdf": "PBKDF2-SHA-256",
      "iterations": 600000,
      "salt": "c3Nzc3Nzc3Nzc3Nzc3Nzcw==",
      "nonce": "bm5ubm5ubm5ubm5u",
      "wrapped_key": "d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3"
    }
  }
}
~~~~

Valid: people

~~~~
{
  "version": "locker.envelope.v1",
  "suite": "AES-256-GCM",
  "chunk_size": 1048576,
  "padded_size": 256,
  "nonce_base": "YmJiYmJiYmI=",
  "lock": "people",
  "slots": {
    "people": [
      {
        "subject": {
          "type": "user",
          "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR"
        },
        "algorithm": "ECDH-P256/HKDF-SHA256/AES-256-GCM",
        "recipient_key": "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8",
        "ephemeral_public_key": {
          "kty": "EC",
          "crv": "P-256",
          "x": "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHg",
          "y": "eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXk"
        },
        "salt": "c3Nzc3Nzc3Nzc3Nzc3Nzcw==",
        "nonce": "bm5ubm5ubm5ubm5u",
        "wrapped_key": "d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3"
      }
    ]
  }
}
~~~~

Rejected: lock-without-slot

~~~~
{
  "version": "locker.envelope.v1",
  "suite": "AES-256-GCM",
  "chunk_size": 1048576,
  "padded_size": 256,
  "nonce_base": "YmJiYmJiYmI=",
  "lock": "password",
  "slots": {
    "link": {}
  }
}
~~~~

Rejected: other-suite

~~~~
{
  "version": "locker.envelope.v1",
  "suite": "AES-128-GCM",
  "chunk_size": 1048576,
  "padded_size": 256,
  "nonce_base": "YmJiYmJiYmI=",
  "lock": "link",
  "slots": {
    "link": {}
  }
}
~~~~

Rejected: padded-size

~~~~
{
  "version": "locker.envelope.v1",
  "suite": "AES-256-GCM",
  "chunk_size": 1048576,
  "padded_size": 100,
  "nonce_base": "YmJiYmJiYmI=",
  "lock": "link",
  "slots": {
    "link": {}
  }
}
~~~~

Rejected: two-locks

~~~~
{
  "version": "locker.envelope.v1",
  "suite": "AES-256-GCM",
  "chunk_size": 1048576,
  "padded_size": 256,
  "nonce_base": "YmJiYmJiYmI=",
  "lock": "link",
  "slots": {
    "link": {},
    "password": {
      "algorithm": "PBKDF2-SHA256/AES-256-GCM",
      "kdf": "PBKDF2-SHA-256",
      "iterations": 600000,
      "salt": "c3Nzc3Nzc3Nzc3Nzc3Nzcw==",
      "nonce": "bm5ubm5ubm5ubm5u",
      "wrapped_key": "d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3"
    }
  }
}
~~~~

Rejected: unknown-field

~~~~
{
  "version": "locker.envelope.v1",
  "suite": "AES-256-GCM",
  "chunk_size": 1048576,
  "padded_size": 256,
  "nonce_base": "YmJiYmJiYmI=",
  "lock": "link",
  "slots": {
    "link": {}
  },
  "secret": "the content key never goes in the header"
}
~~~~

## Validate

~~~~
const contracts = require('openvibe-contracts');
contracts.validate('locker.envelope@1', value);   // { valid, errors: [{ path, message }] }
~~~~
