WebsitePolicy website.policy@1

Generated at from openvibe-contracts v0.419.0 and openvibe-sdk v0.161.0.

Version
1.0.0
Owner
connect
Visibility
public
Status
active
Compatibility
backward
Schema
https://openvibe.network/contracts/website/policy.v1.json

website.policy@1: how a Share decides who may reach it and under what conditions (public, OpenVibe sign-in, named people or projects, password, one-time link, expiry), plus IP/country rules, rate limits, a bot check, a WAF preset and required device posture. mode is the only field required except that expiry also requires a non-null expires_at. id, owner and version are present when Connect returns a stored policy. Password and one-time policies stay inactive until their credential or link is configured through the CON-5 API. A password is stored as a one-way hash beside this record and is never part of this document; password_set only says one exists.

Fields

FieldTypeRequiredDescriptionConstraints
idstringPresent on a stored policy; omitted when the policy is written inline with a new share.
  • pattern ^pol_[0-9A-HJKMNP-TV-Z]{26}$
owneranyThe person who owns the policy; set from the token when a policy is created.
project_idstring | nullThe project scope, when the policy belongs to one.
  • pattern ^prj_[0-9A-HJKMNP-TV-Z]{26}$
namestring
  • minLength 1
  • maxLength 80
modeenumyespublic: anyone with the link after the abuse screen; signin: any OpenVibe account (optionally a project); named: allowed_subjects/allowed_projects only; password: a shared password; one_time: single-use links; expiry: public access only until expires_at. An expires_at may also cap any other mode.
  • one of "public", "signin", "named", "password", "one_time", "expiry"
allowed_subjectsidentity.subject-refExplicit people or agents allowed in named mode; a project's members are allowed through allowed_projects instead.
  • maxItems 500
  • unique items
allowed_projectsarray of stringProjects whose members may reach a named or signin share.
  • maxItems 100
  • unique items
  • items: pattern ^prj_[0-9A-HJKMNP-TV-Z]{26}$
ip_allowarray of stringCIDR ranges allowed before any other rule; empty means no IP allowlist.
  • maxItems 100
  • items: minLength 2
  • items: maxLength 64
ip_denyarray of stringCIDR ranges always refused; evaluated before the allowlist.
  • maxItems 100
  • items: minLength 2
  • items: maxLength 64
country_allowarray of stringISO 3166-1 alpha-2 country codes allowed; empty means all.
  • maxItems 250
  • items: pattern ^[A-Z]{2}$
country_denyarray of string
  • maxItems 250
  • items: pattern ^[A-Z]{2}$
rate_limitsobject
  • no other fields
rate_limits.per_share_per_minuteinteger
  • minimum 1
  • maximum 1000000
rate_limits.per_ip_per_minuteinteger
  • minimum 1
  • maximum 100000
rate_limits.max_connectionsinteger
  • minimum 1
  • maximum 100000
rate_limits.max_request_bytesinteger
  • minimum 1
  • maximum 1073741824
bot_checkenum
  • one of "none", "turnstile"
  • default "none"
waf_presetenumDefense in depth at the edge; Cloudflare's managed rules run in front of it for HTTP/WS.
  • one of "none", "standard", "strict", "api"
  • default "standard"
posture_requiredarray of enumSelf-reported device posture a share may require; a signal, not a guarantee.
  • maxItems 10
  • unique items
  • items: one of "node_current", "os_patched", "firewall_on", "disk_encryption", "no_risky_ports"
password_setbooleanRead-only: true when a password hash exists. The hash itself never leaves Connect.
password_hintstring | nullAn optional hint shown on the password page; never the password.
  • maxLength 120
one_time_max_usesintegerFor one_time mode; 1 unless the owner allows more.
  • minimum 1
  • maximum 1000
session_ttl_secondsintegerHow long a host-only __Host-ov_session on the share host stays valid after sign-in.
  • minimum 60
  • maximum 604800
expires_atstring | nullThe absolute end of the policy; required and non-null for expiry mode, optional for other modes.
  • format date-time
versionintegerThe signed policy version the edge caches and checks on every request; present on a stored policy.
  • minimum 1
created_atstring
  • format date-time
updated_atstring
  • format date-time

Examples

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

Valid: expiry
{
  "id": "pol_01J8Z3Q4R5S6T7V8W9X0Y1Z2A4",
  "owner": {
    "type": "user",
    "id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
  },
  "project_id": null,
  "name": "Launch demo",
  "mode": "expiry",
  "allowed_subjects": [],
  "allowed_projects": [],
  "ip_allow": [],
  "ip_deny": [],
  "country_allow": [
    "DE",
    "FR"
  ],
  "country_deny": [],
  "rate_limits": {
    "per_share_per_minute": 600,
    "per_ip_per_minute": 60,
    "max_connections": 100,
    "max_request_bytes": 10485760
  },
  "bot_check": "turnstile",
  "waf_preset": "standard",
  "posture_required": [],
  "password_set": false,
  "session_ttl_seconds": 3600,
  "expires_at": "2026-11-01T00:00:00.000Z",
  "version": 3,
  "created_at": "2026-10-29T08:00:00.000Z",
  "updated_at": "2026-10-29T09:00:00.000Z"
}
Valid: password
{
  "id": "pol_01J8Z3Q4R5S6T7V8W9X0Y1Z2A4",
  "owner": {
    "type": "user",
    "id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
  },
  "project_id": null,
  "name": "Password for the demo",
  "mode": "password",
  "allowed_subjects": [],
  "allowed_projects": [],
  "ip_allow": [],
  "ip_deny": [],
  "country_allow": [
    "DE",
    "FR"
  ],
  "country_deny": [],
  "rate_limits": {
    "per_share_per_minute": 600,
    "per_ip_per_minute": 60,
    "max_connections": 100,
    "max_request_bytes": 10485760
  },
  "bot_check": "turnstile",
  "waf_preset": "standard",
  "posture_required": [],
  "password_set": true,
  "password_hint": "the one from the call",
  "one_time_max_uses": 1,
  "session_ttl_seconds": 3600,
  "expires_at": null,
  "version": 3,
  "created_at": "2026-10-29T08:00:00.000Z",
  "updated_at": "2026-10-29T09:00:00.000Z"
}
Rejected: bad-mode
{
  "mode": "invite_only"
}
Rejected: missing-expiry
{
  "id": "pol_01J8Z3Q4R5S6T7V8W9X0Y1Z2A4",
  "owner": {
    "type": "user",
    "id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
  },
  "project_id": null,
  "name": "Launch demo",
  "mode": "expiry",
  "allowed_subjects": [],
  "allowed_projects": [],
  "ip_allow": [],
  "ip_deny": [],
  "country_allow": [
    "DE",
    "FR"
  ],
  "country_deny": [],
  "rate_limits": {
    "per_share_per_minute": 600,
    "per_ip_per_minute": 60,
    "max_connections": 100,
    "max_request_bytes": 10485760
  },
  "bot_check": "turnstile",
  "waf_preset": "standard",
  "posture_required": [],
  "password_set": false,
  "session_ttl_seconds": 3600,
  "version": 3,
  "created_at": "2026-10-29T08:00:00.000Z",
  "updated_at": "2026-10-29T09:00:00.000Z"
}
Rejected: null-expiry
{
  "id": "pol_01J8Z3Q4R5S6T7V8W9X0Y1Z2A4",
  "owner": {
    "type": "user",
    "id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
  },
  "project_id": null,
  "name": "Launch demo",
  "mode": "expiry",
  "allowed_subjects": [],
  "allowed_projects": [],
  "ip_allow": [],
  "ip_deny": [],
  "country_allow": [
    "DE",
    "FR"
  ],
  "country_deny": [],
  "rate_limits": {
    "per_share_per_minute": 600,
    "per_ip_per_minute": 60,
    "max_connections": 100,
    "max_request_bytes": 10485760
  },
  "bot_check": "turnstile",
  "waf_preset": "standard",
  "posture_required": [],
  "password_set": false,
  "session_ttl_seconds": 3600,
  "expires_at": null,
  "version": 3,
  "created_at": "2026-10-29T08:00:00.000Z",
  "updated_at": "2026-10-29T09:00:00.000Z"
}
Rejected: secret-field
{
  "mode": "password",
  "password_hash": "$argon2id$v=19$m=65536,t=3,p=4$example$example"
}

Validate

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