Repositories openvibers openvibe.quest official

openvibers/openvibe.quest

Open source quest log for the OpenVibe network: real things you do on OpenVibe sites complete quests that pay badges and OpenCoins.

Get it

Get this repository

Clone over HTTPS

A Git token is made for this copy and shown once. It can read and write the repositories you own, and expires in an hour.

git clone https://git.openvibe.codes/openvibers/openvibe.quest.git
Clone to my computer

OpenVibe Link asks you on your computer. The folder is inside the root you chose in Link, and the request ends after two minutes.

Clone to my computer
Open in Codes

A Codes session clones from our disk: no network, no GitHub quota. When this repository has a GitHub twin, Codes pushes there too.

Open in Codes

README.md

View file

OpenVibe.Quest

Quests across the whole network.

Live site: https://openvibe.quest · API docs: https://openvibe.services · For AI: https://openvibe.quest/llms.txt
Run it: npm ci && npm run dev (Node 22 or newer) · Tests: npm test

Status: first release. The quest log, the events consumer, the OpenCoins crediting, the item rewards and the pages
are written and tested; it is deployed, and OpenCoins have been credited in production since 2026-10-14
(QUEST_COINS=on, within the daily caps).
Domain: openvibe.quest · Port: 4980 · Service id: quest · Env prefix: QUEST
License: AGPL-3.0 (same as every OpenVibe service).

What it is

One quest log across every OpenVibe site. A quest counts something a person really did somewhere in the network —
went live, started a thread, said hello in a public chat room, edited a wiki page — and pays a badge on their
profile. When OpenCoins are on it also credits OpenCoins through OpenVibe.Network's wallet. OpenCoins are
loyalty points, never money, and they are never Vibes (Vibes are real money and belong to the sites that
pay them out); nothing here reads or moves money.

Quest runs no activities of its own. It listens to the domain events the sites already emit and nothing else: no
page views, no clicks, no watch time, and no content a person wrote — not a message's text, not a thread's title.
/how-it-works prints the complete list of what is listened to, generated from the same catalog the consumer
subscribes to.

Owns

OpenVibe.Quest owns the quest log and the catalog that describes it.

The home page leads with one shared daily quest. A due-based job chooses three verified Events step types across at least two sites, asks OpenVibe.AI to favor recent network changes, and rotates a hand-written set when AI is unavailable. GET /api/v1/daily is public; GET /api/v1/me/daily returns the caller's private checklist and streak. PUT /api/v1/me/daily/time-zone stores an IANA time zone for local-day counting. Apps with quest.steps.contribute can POST /api/v1/step-types; staff must approve a proposal before the generator can use it.

/me shows progress, a counted Done fold and earned and locked glyph medallions; /quests leads with today's quest before the site groups. Both pages render their words and links on the server. Daily quest titles and app badges request a missing glyph from OpenVibe.Design's service draft API with design.glyph.draft; pending drawings stay in staff review and the bundled glyph remains visible. Once approved, Quest uses Design's validated SVG image. Set OV_DESIGN_INTERNAL_URL for the internal Design service or QUEST_DESIGN_DRAFTS=off to disable requests during a rollout. Network must grant the Quest service principal design.glyph.draft before enabling draft requests.

  • The quest catalog in code ([server/quests/catalog.js](server/quests/catalog.js)): 12 first-party quests, each
    with its steps, the events that count them, its badge, its OpenCoins and any item reward, plus the list of events
    Quest listens to. A quest may not count an event the catalog does not document; that fails at load.
  • quest_progress ([migrations/0002_quest.sql](migrations/0002_quest.sql)): one row per (person, quest, step) — how
    far they are.
  • quest_completions ([migrations/0002_quest.sql](migrations/0002_quest.sql), extended by
    [migrations/0004_item_rewards.sql](migrations/0004_item_rewards.sql)): one row per (person, quest) — the completion,
    its OpenCoins state and its item-grant state.
  • quest_badges ([migrations/0002_quest.sql](migrations/0002_quest.sql)): one row per (person, badge).
  • quest_event_inbox ([migrations/0002_quest.sql](migrations/0002_quest.sql)): this service's own receipts of the
    OpenVibe.Events deliveries it applied.
  • account_data_events ([migrations/0003_account_data.sql](migrations/0003_account_data.sql)): its own receipts of
    the ADR-033 account export and deletion deliveries.

It is the authority for a person's quest progress, completions and badges, and for the quest.* namespace. Migration
0001_initial.sql creates no table.

Does not own

  • Identity — the usr_… subjects, the Network user id and the sign-in session are OpenVibe.Network's. Quest
    stores the subject a delivery names and resolves the wallet id on demand; it issues and verifies no identity itself.
  • Events — the events and their delivery belong to OpenVibe.Events. Quest runs no activity of its own and only
    consumes what Events delivers.
  • Items and their definitions — the items Quest grants (Sparkle, Hearts, Basic Cap, Fire Name, Ice Name, Rainbow
    Name) are OpenVibe.Live's, and the instances are OpenVibe.Inventory's. Quest holds no item row.
  • The wallet and the OpenCoins ledger — the balance lives in OpenVibe.Network's wallet. Quest holds no balance and
    reads none.
  • The sites' content — threads, posts, comments, chat messages, wiki revisions, streams and job inputs belong to
    OpenVibe.Community, OpenVibe.Space, OpenVibe.Chat, OpenVibe.Wiki, OpenVibe.Live and OpenVibe.Tools. Quest counts
    that they happened and stores none of them.

Depends on

| Depends on | For | Env vars |
|---|---|---|
| OpenVibe.Network | sign-in with PKCE S256 (OAuth client quest), its JWKS, identity.subject.resolve, network.coins.credit, and the account export/deletion sender | OV_NETWORK_URL, OV_NETWORK_INTERNAL_URL, OV_OAUTH_CLIENT_ID, OV_OAUTH_CLIENT_SECRET |
| OpenVibe.Events | the deliveries to POST /internal/events and the subscriptions it creates at boot | QUEST_EVENTS_URL, QUEST_EVENTS_SECRET, QUEST_EVENTS_ENDPOINT |
| OpenVibe.Inventory | granting an item reward, and reading Live's item definitions by alias | OV_INVENTORY_INTERNAL_URL |
| PostgreSQL | the store (every migration and table) | DATABASE_URL, DATABASE_DIRECT_URL |
| Valkey | shared per-caller limit counters (optional: without it they count in this process) | VALKEY_URL, VALKEY_PREFIX |

Packages: openvibe-contracts (service auth, capabilities, HTTP problems), openvibe-sdk (auth, the events
inbox, account-data, limits, service lifecycle) and openvibe-shared (cache policy, metrics, readiness, release,
legal pages).

Capabilities

The service manifest lists no capabilities of its
own
and produces no events; no /api/v1 route is guarded by one ([server/http/principal.js](server/http/principal.js)
CAPABILITIES is empty). Quest calls these on other services with its own client-credentials token:

  • identity.subject.resolve — OpenVibe.Network GET /internal/identity/resolve, to turn a subject into the wallet's
    network_user_id ([server/quests/coins.js](server/quests/coins.js)).
  • network.coins.credit — OpenVibe.Network POST /internal/coins/credit, to credit a completion's OpenCoins.
  • events.subscription.manage — OpenVibe.Events, to create the catalog's subscriptions at boot
    ([server/events-consumer.js](server/events-consumer.js)).
  • inventory.item.grant — OpenVibe.Inventory POST /api/v1/grants, to give a quest's item
    ([server/quests/items.js](server/quests/items.js)).

What works

| Piece | Where | What it does |
|---|---|---|
| Catalog | [server/quests/catalog.js](server/quests/catalog.js) | 36 first-party quests in code (the 18 of the Live setup series among them), one per real thing a person can do, plus the whole list of events Quest listens to (and the one it does not). A quest may not count an event the catalog does not document: that fails at load |
| Events consumer | [server/events-consumer.js](server/events-consumer.js) | POST /internal/events, exactly as OpenVibe.Space does it: raw body, parseDelivery(…, { requireV2: true }), the openvibe-sdk inbox for once-only application, forwarding headers refused, 503 without QUEST_EVENTS_SECRET. It subscribes to the catalog's events at boot (idempotent, off without QUEST_EVENTS_URL) |
| What moves a quest | [server/quests/apply.js](server/quests/apply.js) | The person is taken from the field the event's own schema defines (channel.subject, follower, author, user_subject, player.subject, owner; the envelope actor only where the schema says the actor is that person). No subject, a gst_ guest, or a service/app/mod principal → ignored, nothing written |
| Quest log | [server/quests/store.js](server/quests/store.js), [migrations/0002_quest.sql](migrations/0002_quest.sql) | Progress per step, one completion per person per quest (unique), one badge per person, and the Events receipts. Two deliveries of the same event change nothing; so does a replay of the whole request |
| Item rewards | [server/quests/items.js](server/quests/items.js) | Six quests also give one of Live's items: Sparkle, Hearts, Basic Cap, Fire Name, Ice Name and Rainbow Name. Each is granted in OpenVibe.Inventory with Quest's own token (Live names Quest a grantor of those items, ADR-054 §3) as earned, keyed by the completion id. Failures and completions from before items are retried by a timer. QUEST_ITEMS=off turns it off |
| OpenCoins | [server/quests/coins.js](server/quests/coins.js) | Credits a completion through OpenVibe.Network's wallet with idempotency_key = <completion id>. Off by default, per-person and network-wide daily caps, failures recorded with their reason and retried by a timer |
| API | [server/http/api.js](server/http/api.js) | GET /quests, GET /quests/:id, GET /me/quests, GET /profiles/:subject/badges (see the table below) |
| Pages | [server/http/pages.js](server/http/pages.js), [server/render/quests.js](server/render/quests.js) | Home (the quests grouped by site, how progress works), a quest, your quest log (plain <progress> bars), the rules, the update log — all server-rendered, all readable without JavaScript |
| Discovery | [server/http/discovery.js](server/http/discovery.js) | robots.txt (the private log disallowed), sitemap.xml (every quest page), llms.txt, llms-full.txt, JSON-LD |
| Request telemetry | [server/app.js](server/app.js), [server/index.js](server/index.js), [migrations/0006_telemetry.sql](migrations/0006_telemetry.sql) | Every HTTP request is aggregated by route template, method and status class, never by raw URL. Once a minute one http.request sample per key, plus the active-requests, p95 latency and event-loop-lag gauges, is written to this service's own telemetry_samples table (openvibe-sdk 0.46 telemetry, the platform.telemetry-sample@1 shape); rows older than 14 days are deleted daily. /shared files, static assets, health, readiness and /metrics are not counted. server/app.js builds the collector (createHttpTelemetry) and mounts its middleware; server/index.js flushes it every minute and on stop, and runs the 14-day retention |
| Foundation | server/app.js, server/auth/, server/db.js, server/observability.js, deploy/ | The skeleton's plumbing, unchanged: Network SSO with PKCE, per-caller limits, PostgreSQL migrations, truthful /api/ready, nginx and systemd files |

API

| Route | Who | |
|---|---|---|
| GET /api/v1/ping | anyone | { ok: true, service: "quest" } |
| GET /api/v1/quests | anyone | the catalog grouped by site (sites[].quests), a flat quests[], and whether OpenCoins are live with their caps |
| GET /api/v1/quests/:id | anyone | one quest: its steps, its events, its reward |
| GET /api/v1/me/quests | a signed-in person | your log: progress per step, completions (with what happened to their coins) and your badges |
| GET /api/v1/profiles/:subject/badges | anyone | a person's badges: id, name and date — nothing else, ever |
| PUT /api/v1/app-quests/:slug | a developer app (quest.app_quest.manage) | create or replace one of the app's quests (quest.app-quest-request@1 → quest.app-quest@1) |
| GET /api/v1/app-quests | a developer app (quest.app_quest.manage) | the project's app quests, with how many people finished each |
| DELETE /api/v1/app-quests/:slug | a developer app (quest.app_quest.manage) | retire one: no longer counted or listed; its completions and badges stay |

Public reads need no token. /me/quests takes a person's Network token as a Bearer or this site's session, and
answers only that person's rows. Errors are RFC 9457 application/problem+json with a stable code. Requests are
counted per caller (a person as themselves, anyone else by address) with the default read numbers —
[server/http/caller-limits.js](server/http/caller-limits.js) BUDGETS is empty because no route is expensive yet.

How progress happens

OpenVibe.Events delivers an event when a site really does something. Quest counts these, and only these:

| Event | From | The person is |
|---|---|---|
| live.stream.started | OpenVibe.Live | the channel's user subject (else the envelope actor) |
| network.follow.created | OpenVibe.Network | the follower |
| community.thread.created / community.post.created / community.comment.created | OpenVibe.Community | the author |
| space.thread.created | OpenVibe.Space | the author |
| chat.room.message.created | OpenVibe.Chat | payload.user_subject (public rooms only; a DM never reaches Quest) |
| wiki.revision.created | OpenVibe.Wiki | the author (a service edit is ignored) |
| games.player.joined | OpenVibe.Games | the player's account subject (a guest is ignored) |
| tools.job.created | OpenVibe.Tools | the owner (an anonymous session is ignored) |
| live.setup.task_completed | OpenVibe.Live | payload.user, the streamer who finished a task on Live's go-live setup list |
| blog.post.published | OpenVibe.Blog | the envelope actor (a schedule or a service publishing is nobody) |
| deals.offer.created / deals.vote.changed | OpenVibe.Deals | the envelope actor: the submitter or the voter (an imported deal's actor is svc:deals) |
| run.job.succeeded | OpenVibe.Run | payload.requester when it is a user (an app's or a service's job is nobody's) |
| inventory.item.equipped | OpenVibe.Inventory | payload.owner |
| bot.robot.online | OpenVibe.Bot | payload.owner, the robot's owner (Bot names it since contracts 0.192.0) |
| games.world.published | OpenVibe.Games | payload.owner, who published a creator world for the first time (contracts 0.194.0) |
| mediahub.video.published | OpenVibe.Video (MediaHub) | payload.owner, whose upload is public and ready for the first time (contracts 0.196.0) |
| mediahub.picture.uploaded | OpenVibe.Pics (MediaHub) | payload.owner, who uploaded a picture while signed in (contracts 0.196.0) |

App quests (contracts 0.193.0, [server/quests/app-quests.js](server/quests/app-quests.js)): a developer app defines
quests on its own project's events with an app token (audience openvibe.quest, capability quest.app_quest.manage).
A step may count only app.<project_key>.<name> events of that project, and the person is the event's actor (an app
publishing on_behalf_of a user; an app acting as itself is nobody). Only production counts: Events never hands
sandbox events to a first-party consumer, so a sandbox app's quests are its own and never listed. Quest subscribes to
app.<project_key>.* when a project's first production quest goes active (and at boot). The reward is a badge named
for the app, never OpenCoins; at most 20 active quests a project; /apps/quests/<id> is the public page and the home
page lists them under "Made by apps".

A step can also name payload values to match ({ task: 'emote' }): the "Set up your channel" series is one
quest per Live setup task, plus one that counts all 18 tasks. The home page shows a series as one card; its page is
/series/<id>. A quest completes when every step reaches its count. The completion, its badge and (when OpenCoins are on) its
coins are recorded once; a redelivery of the same event, or the whole request again, changes nothing.

Data sources and their terms

| Source | Used for | Terms as applied |
|---|---|---|
| OpenVibe.Events (openvibe.events) | the domain events that move the log, POST /internal/events, loopback only | Signed deliveries only: X-OpenVibe-Signature-V2 (HMAC-SHA256 over <timestamp>.<raw body>), a 300-second replay window, requireV2, the secret from QUEST_EVENTS_SECRET (32+ characters, comma-separated for rotation). A delivery that carries a forwarding header is refused: nginx never proxies this route. Once-only application is the openvibe-sdk inbox, in the same transaction as the writes |
| openvibe-contracts v0.190.0 | the shape of every event and of the two Network calls | Every field read is the one the schema at contracts/events/payloads/<type>.v1.json defines; no field is inferred and nothing about a person is taken from a name or from the request |
| OpenVibe.Network (openvibe.network) | resolving a subject to its wallet id, and crediting OpenCoins | GET /internal/identity/resolve (capability identity.subject.resolve, identity.resolve-result@1) gives network_user_id; POST /internal/coins/credit (capability network.coins.credit, body network.coins-change-request@1, answer network.coins-balance-result@1) credits, with app_id: quest — a service token may only name its own app id. Both use this service's own client credentials over the internal URL; no token is logged |
| OpenVibe sites (Live, Community, Space, Chat, Wiki, Games, Tools, Network, Blog, Deals, Run, Inventory, Bot) | nothing is fetched from them | Quest makes no outbound request to any site, and no URL from a request ever decides one: the only host it calls is OV_NETWORK_INTERNAL_URL |

Rewards are badges and OpenCoins. Nothing here is money, and nothing here is Vibes.

Configuration

See [.env.example](.env.example) for every name. Required in production: OV_OAUTH_CLIENT_SECRET, BASE_URL,
DATABASE_URL and DATABASE_DIRECT_URL. The product adds:

| Variable | Default | What it does |
|---|---|---|
| QUEST_EVENTS_SECRET | unset | signs the Events deliveries (32+ characters; comma-separated for rotation). Unset turns the consumer off (503) and the quest log then never moves |
| QUEST_EVENTS_URL | unset | OpenVibe.Events' base URL, used once at boot to create the subscriptions for the catalog's events |
| QUEST_EVENTS_ENDPOINT | http://127.0.0.1:<port>/internal/events | the endpoint the subscriptions name |
| QUEST_COINS | off | on credits rewards through Network's wallet; off records completions with coins_state=skipped and the pages say the rewards start soon |
| QUEST_COINS_DAILY_CAP | 50 | OpenCoins per person per UTC day; over it a completion is skipped with the reason |
| QUEST_COINS_GLOBAL_DAILY_CAP | 5000 | OpenCoins for everyone together per UTC day |
| QUEST_COINS_RETRY_MS | 300000 | how often a failed or unfinished credit is retried (only while QUEST_COINS=on) |

Development

npm install
fnm exec --using=22 npm test        # every test/*.test.js, on temp PGlite databases with a mock Network
fnm exec --using=22 npm run dev     # http://localhost:4980

Without DATABASE_URL development uses an embedded PGlite database in data/pglite (one process only). npm run test:pg runs the same suite through PostgreSQL and PgBouncer (see [.github/workflows/ci.yml](.github/workflows/ci.yml)).

The mock Network in [test/helpers/mocks.js](test/helpers/mocks.js) also serves /internal/identity/resolve and
/internal/coins/credit, checked as Network checks them (a svc:quest token with the capability, its own app id,
idempotent by key), so the OpenCoins tests need no real wallet.

Acceptance

npm test ([test/run.js](test/run.js)) runs every test/*.test.js in its own process on temp PGlite databases and
an in-process mock of OpenVibe.Network ([test/helpers/mocks.js](test/helpers/mocks.js)); no network or running site is
needed. npm run test:pg runs the same suite through PostgreSQL and PgBouncer. The main proofs:

  • [test/events-consumer.test.js](test/events-consumer.test.js) — a bad signature, a forwarded request and an
    unconfigured secret are refused; the same event twice changes nothing; progress counts to completion and the badge
    is awarded once; an event that names nobody creditable is ignored; OpenCoins off is skipped, on credits with the
    completion id as the idempotency key, the two daily caps stop it, and a failure is recorded and retried.
  • [test/quests-api.test.js](test/quests-api.test.js) — the catalog and a quest are public; /me/quests requires
    sign-in and shows only the caller's rows; the badge endpoint answers an id, a name and a date and nothing else; the
    pages are complete without JavaScript; a hostile title is escaped.
  • [test/items.test.js](test/items.test.js) — a quest's item is granted in a stand-in Inventory with origin: earned
    and the completion id, a refusal is recorded and retried, a completion from before items is granted by the retry,
    and QUEST_ITEMS=off grants nothing.
  • [test/account-data.test.js](test/account-data.test.js) — an export carries only the person's own progress,
    completions and badges; a deletion erases them whole and confirms the counts; a redelivery changes nothing.
  • [test/security-secrets.test.js](test/security-secrets.test.js) — the OAuth client secret never appears in a
    response, the database or a log line, over every route and hostile input.
  • [test/security-session.test.js](test/security-session.test.js) — a FedCM assertion, app, service or internal token
    is never accepted as a session cookie.
  • [test/caller-limits.test.js](test/caller-limits.test.js) — past its budget a caller gets 429 problem+json with
    Retry-After before the route does any work, while another caller still passes.
  • [test/open-redirect.test.js](test/open-redirect.test.js) — the sign-in next never leaves the site.
  • [test/no-internal-key.test.js](test/no-internal-key.test.js) — no X-Internal-Key anywhere in the code, deploy
    files or the running service (ADR-014).
  • [test/auth-ops.test.js](test/auth-ops.test.js) — PKCE S256 sign-in, truthful /api/ready, /release.json, and
    loopback-only /metrics.
  • [test/auth-jwks.test.js](test/auth-jwks.test.js) — the Network signing key is kept across outages, a rotation is
    honoured at once and unknown-kid floods are throttled.
  • [test/discovery.test.js](test/discovery.test.js) — robots.txt, sitemap.xml, llms.txt, llms-full.txt and the
    JSON-LD carry real public entries only and take lastmod from STATUS.json, not the clock.
  • [test/service-kit.test.js](test/service-kit.test.js) — the graceful stop runs its close steps in the manifest's
    order and resolves exit 0.
  • [test/layout.test.js](test/layout.test.js), [test/asset-cache.test.js](test/asset-cache.test.js),
    [test/nginx-auth-limit.test.js](test/nginx-auth-limit.test.js) and [test/perf-budget.test.js](test/perf-budget.test.js)
    — the shared boost marker and the asset cache rules, the nginx limit zones, and the home page's size budgets.

Deploy (for the lead)

  • Deploy: sudo ovhost deploy quest on the host (git checkout at /opt/openvibe.quest, unit
    openvibe-quest.service on 127.0.0.1:4980, env /etc/openvibe/quest.env, database ov_quest on the data role).
  • nginx: [deploy/nginx/openvibe.quest.conf](deploy/nginx/openvibe.quest.conf), installed with ov-vhost-install.
    /internal/ is answered 404 from outside; Events posts to the loopback address directly.
  • Rollback: ovhost puts the previous sha back by itself when /api/ready does not answer after the restart.
  • Before OpenCoins can be turned on, OpenVibe.Network must grant the quest OAuth client
    identity.subject.resolve and network.coins.credit (its service-principal grants), and QUEST_EVENTS_SECRET
    and a subscription for the catalog's events must exist at OpenVibe.Events.
  • Register the service and its capabilities in OpenVibe.Contracts (contracts-service: quest in CI) and with
    OpenVibe.Services before the first deploy.

Account export and deletion

A person's account at OpenVibe.Network can be exported and deleted, and every service holding their rows answers its
part (ADR-033). Quest receives network.account.export_requested and network.account.deleted at POST /internal/events
(loopback only) through the consumer it already runs — the two account topics are handed to
[server/identity/account-data.js](server/identity/account-data.js), which maps the tables, and the boot-time
subscriptions for them are created (alongside the catalog's events) by
[server/events-consumer.js](server/events-consumer.js):

  • Exported: the quests a person is partway through (progress.json), the quests they completed and their OpenCoins
    (completions.json) and the badges they earned (badges.json), pushed to POST /internal/account-exports/:id/parts
    with this service's own token. Nothing here is a secret — Quest stores no token, key or credential.
  • Erased: all three tables hold the person's own rows and nothing anyone else's page hangs under them, so they are
    deleted whole and nothing is kept. Deleting a completion deletes its OpenCoins record here; the wallet credit at
    OpenVibe.Network is Network's own data. Quest then confirms with POST /internal/account-deletions/:id/confirmations
    and the counts.
  • Anonymized: nothing. There is no row Quest keeps that was written by this person for another person to read.

The delivery receipts (quest_event_inbox) are this service's own, not a person's rows, and are neither exported nor
erased.

Environment: QUEST_EVENTS_SECRET (comma-separated for rotation, 32+ characters each; unset makes the route answer
503), QUEST_EVENTS_URL (or EVENTS_URL) is where the subscriptions are created at boot (off when unset), and
QUEST_EVENTS_ENDPOINT overrides the loopback endpoint; QUEST_EVENTS_SUBSCRIBE=0 turns the boot-time subscription off.

Security (threat notes)

Reporting a vulnerability: [SECURITY.md](SECURITY.md).

  • Session tokens are httpOnly cookies; a FedCM assertion or an app or service token is never a session.
  • Secrets live only in the env file; only environment variable names appear in code and docs, and no secret is
    logged. A wallet or identity failure is recorded as Network's own short code, never as a token or a body.
  • Request bodies are never logged. The Events route is the one place a body is read raw, and only to verify its
    signature.
  • Nothing in a request may decide a URL this service fetches: the only host Quest calls is the configured
    OV_NETWORK_INTERNAL_URL.
  • A person's log is theirs: /me/quests answers their rows only, and the public badge endpoint answers an id, a
    name and a date and nothing else.

Part of the OpenVibe network. Built in the open by OpenVibers.

<!-- versions:start -->

  • openvibe-contracts: v0.419.0
  • openvibe-sdk: v0.161.0
  • openvibe-shared: v4.235.0
  • openvibe-publishing: v1.7.0

<!-- versions:end -->