Ask Mascot
Start the system, then open it from there
This copy of the studio isn't connected to a running system. On your computer, in the system folder, run
docker compose up, then open http://localhost:8001. The Docs screen works anywhere.
Configure Ask Studio
Where the data comes from (data feeds and the Locations that hold them), who may know what (Realms and people), and how it's used.
Admin shows the businesses your account looks after: their characters, Realms and Locations. A change here applies wherever it is used, not only to the character this page is open for.
Admin needs a running system. Start it with
docker compose up and open http://localhost:8001.
Sign in to Admin
Admin, Insights and the audit trail show the questions people asked and every setting, so they need your own admin account. Asking a character doesn't.
Signed in as .
How Ask Studio is organised
- Data feed
- One source of data: uploaded files, a folder, a database connection or an API. Everything the characters know comes from data feeds.
- Location
- Where data feeds live (a computer, a server, a cloud account), with a small agent that indexes them.
- Index run
- One indexing run. Each leaves a receipt, and every answer names the index run its passages came from.
- Realm
- An audience, such as Finance or Everyone. For each data feed it decides how much people see: full rows, masked rows, totals only, the name only, or nothing.
- People
- They ask in the Realms they belong to. Their access groups (staff, finance) decide which records a data feed lets them read.
A Location is a place that holds your data, with a small agent that indexes it and links in to Ask Studio. Update now runs an index run for new and changed records (a website is crawled again; only if its pages changed is it digested again and its program catalog rebuilt); Keep up to date does the same every day or week; Rebuild everything reads every record again; Delete Location removes the Location from Ask Studio. Its data feeds are configured on the Data feeds tab. Nothing here deletes your files.
Add a Location
A Location holds one business's data (a website, a folder of files, a database) and has a small agent that indexes it. Adding one takes four steps.
- Start it. On the computer running Ask Studio, in its
systemfolder, run:It creates the Location, registers it, and starts its agent. Your own services indocker-compose.override.ymlare kept. - Check it linked. Within a minute it appears below as Linked. Then set its Bill to on its card.
- Add its data. A website, database or API: Data feeds › Add a data feed. A folder: Digest,
folder
/brain. - Share it. Access › Realms: add it to its business's Realm and choose how much each Realm sees. Then the characters that answer from that Realm can use it.
Its agent runs on another computer?
Loading…
Turn a whole folder into data feeds.
Pick a folder and Ask Studio looks through every file, sorts them into data feeds, flags anything that looks like a password, and suggests how much each Realm sees of each one. You check it in four short steps, then approve.
A website data feed is digested here on its own after every crawl: its pages are sorted into the sections of the site, one data feed each, and the plan shows up below to check or change.
Realms
A Realm is an audience. Open one to set how much of each data feed it sees, and to browse what it knows.
People
People ask in the Realms they belong to. Access groups decide which records a data feed lets them read.
Ask Studio is one system with characters plugged into it. Each character is a module of its own: a look (its
pack), a name, a voice, a greeting, an intro line, suggested questions, and its data (the Realms it answers from, and through them
the Locations and data feeds it can read). Give two characters the same Realms and they answer the same way; give one a Realm of its
own and it answers only from that data. A page shows the character it is opened for: the widget's character attribute,
or a link ending in ?character=.
Add a character
Give a character data of its own
The system writer: it writes the answers of every character that doesn't have a writer of its own (Admin › Characters), and Ingest and Digest use it. Whatever writes them, answers only use passages the person asking may read, and every sentence is checked against its sources.
Rules that bound what every character answers, set per Realm (and a default for all Realms). They apply to all characters, not only the one this page is for. They run on every question: on the question before anything is searched, on the sources before the writer reads them, and on the answer before anyone sees it. Every sentence must cite a source the person may read; that rule is always on.
Try a question
Recent guardrail events
Loading…
How each character is used: the data it can answer from, the questions people ask it, how well it answers and what they think of its answers. All characters together, or one at a time.
Loading…
Businesses
Every Location, Realm and character belongs to one business, and a character answers only from its own business's data. A business's web domains are its own websites: answers may link to them.
Admin accounts
A system admin sees every business and manages accounts. A business admin sees and changes only the businesses chosen for them. Passwords are never shown; set a new one to reset it.
Single sign-on
Let admins sign in with their organisation's identity provider, through OpenID Connect or SAML 2.0. Each connection signs in the e-mail domains you list, for one business or for the whole system, and gives Admin only to the groups you name. Register this service at the IdP with the addresses each connection shows.
Audit log
Every change made in Admin: who made it, when, and for which business. It can't be edited.
Loading…
My account
How Ask Studio works
What the system does and how to use it, the character behind it, and how to build on it. Everything here describes the code as it runs.
Answers from your own data, limited to what each person may see
Data feeds bring data into Locations, where it is indexed and stays. A Realm decides who may know what, and each character answers only from its own Realms. When someone asks, Mascot answers only from what that person may read, inside the guardrails set for that Realm, cites every statement, and says what it had to leave out.
- Data feeds bring data in. A data feed is uploaded files, a folder, a database table or query, a JSON API or a website. Every record keeps its readers, the access groups allowed to read it at the source. A record with no readers is rejected, never indexed.
- An index run indexes it inside the Location. The Location's agent reads each data feed and builds a keyword index (SQLite FTS5) and a vector index (local embeddings), with a second copy where sensitive fields are masked. Each index run leaves a receipt, and every answer names the receipt its passages came from.
- The Location links out. Its agent opens a connection out to the Realm and opens no ports. Only results a person may read ever leave the Location.
- Each character answers from its own Realms. A character is given Realms in Admin › Characters, and answers only from them, so one business's data stays with that business's characters. Its voice only changes the wording: every fact still comes from the sources, and the same guardrails apply.
- The question is checked first (guardrail ①). Before anything is searched, attempts to override the rules, pull out the instructions or reach other people's data are blocked, or the attacking sentences removed and the rest answered. Topics the Realm never discusses are refused. Personal details someone types (ID and card numbers, email addresses, phone numbers) are masked before the question is searched, written about or stored.
- The Realm plans and searches. It checks the person belongs to the Realm, picks the data feeds that could answer, and asks only those Locations, with a signed token holding the person's access groups that expires in 5 minutes.
- The sources are checked (guardrail ②). Pages and files are data, never instructions: sentences in them written as instructions to an AI are taken out before the writer reads them, and secrets and ID numbers are masked.
- Mascot writes the answer from those sources alone, citing every statement. A statement without a valid citation is removed, and anything held back is named rather than hidden.
- The answer is checked before anyone sees it (guardrail ③). A question the Realm's data doesn't cover is off topic: blocked with a message saying what this Realm covers, or answered with a warning. Answers with a blocked phrase, or that repeat the instructions, are replaced; secrets, ID and card numbers are masked; links that don't come from the sources are taken out; and the Realm's tone, house style and closing note are applied. Every answer gets a guardrail report and a verdict: allowed, shaped, safeguarded, warned or blocked.
Set it up in Admin: Ingest (data feeds, Digest, Locations), Access (Realms, people), Characters (which Realms each answers from), then Guardrails (per Realm). Then ask on Ask Mascot.
Data feeds and index runs
A data feed is one thing Ask Studio reads. An index run is one run that reads data feeds into a Location's index. Add data feeds in Admin › Ingest › Data feeds, or turn a whole folder into data feeds with the Digest.
| Kind | What it reads | Good to know |
|---|---|---|
| Files | Uploaded .docx, .pdf, .md and .txt files | Up to 10 MB each. For bigger files or many of them, use a Folder. |
| Folder | Documents in a folder the Location can see | Only inside the Location's data folder or its
source folders (ASK_SOURCE_ROOTS); anything else is refused. |
| Database | One table, or one read-only SELECT, from a SQLite file or a database URL (PostgreSQL, MySQL and others) | Write passwords as ${NAME}; the Location fills them from its own environment. SQLite files are held
to the same folders as a Folder. |
| API | JSON records from a web service (GET), at a dotted path such as
data.items | Headers such as Authorization: Bearer ${TOKEN} keep the secret in the Location. Follows
a next-page link for up to 20 pages. |
| Website | The pages of a public website, and the PDFs it links to, read the way a visitor sees them | Uses the site's sitemap and its own links, stays on the site, obeys robots.txt and skips menus and footers. Stops at a page limit (300 by default). Each index run reads the site again; answers link to the page. |
Readers
Every data feed says who may read its records at the source: either access groups for the whole data feed (for example staff,
finance), or a permissions field on each record. Permissions are re-read every 60 seconds (ASK_ACL_SYNC_SECONDS), so
a change applies without re-indexing.
Index runs
- Update (new and changed) on a Location reads every record, re-embeds only new and changed ones, refreshes permissions and removes records deleted at the source. Rebuild everything on a Location, or Re-index on one data feed, re-embeds every record and stamps it with the new receipt.
- Before reading, each data feed's connection is checked. A failing data feed is skipped and noted on the receipt; the rest go ahead.
- The receipt records, per data feed, how many records were read, rejected, new, changed, deleted and embedded.
The Digest
- Point it at a folder and it sorts every file into data feeds by what it is, its department and its sensitivity, using the files' own labels first.
- Files that look like they hold a password or key are held back. Files with pay or cash figures go into a Confidential data feed that only Leadership can read; Finance and Systems build see only its name.
- An AI model can tag files, but it may only change a tag after matching known answers at least 90% of the time over 15 or more checks. It never lowers sensitivity, and widening access always waits for you.
- You check the files, set how much each Realm sees, and approve. Index runs then run foundation first.
Removing things
Removing a data feed takes its records out of the index; its files are never touched. A data feed from a Location's config file can be brought back. Deleting a Location removes it from every Realm and refuses its agent; its files and index stay on its own machine.
Realms and permissions
Two locks decide what someone sees. The Realm's share level says how much of a data feed that audience gets; the record's readers say whether this person may read that record at all. Both are checked inside the Location, before anything is ranked.
| Share level | The Realm… | In answers |
|---|---|---|
| ●Full | can read every field | Passages quoted in full |
| ◐Some fields hidden | can read it, with sensitive fields replaced by ••• | Passages quoted from the masked copy |
| ΣTotals only | sees totals, never the records | Totals computed inside the Location |
| ○Name only | knows it exists, can't open it | Named as something left out |
| ⊘Hidden | doesn't know it exists | Never mentioned |
Rules worth knowing
- Totals protect individuals. Totals are computed inside the Location. When a total would include records the person can't read, or the Realm sees Totals only, a total over fewer than 3 records is withheld. Records the person can't read are only counted when at least 3 of them are summed; otherwise the total counts only what they can read and says the rest were left out.
- All I can see. Asking across every Realm a person belongs to uses the best level any of them grants, and searches each data feed under the Realm that grants it.
- Sharing brings the Location along. Sharing a data feed with a Realm adds its Location to that Realm.
- Realms can sit under another (for example Finance under Leadership), to group them.
- Catalog and knowledge files. Each Realm's catalog lists only the data feeds it may know about, with record counts and freshness. The same view is written as knowledge files (markdown with front matter) for other AI tools. Only the Realm's members, and Admin, can read them.
- Audit. Every question is written to the Realm's audit log and to each Location it reached.
Set levels in Admin › Access › Realms (open a Realm) or on a data feed's Configure. Changes apply to the next question, and can be undone.
Asking Mascot
Pick who is asking and in which Realm, then ask in plain words (up to 2,000 characters). The person picker lets an admin see exactly what each person would get.
How Mascot finds things
- Each Location searches by keyword (BM25) and by meaning (vectors), and merges the two lists with Reciprocal Rank Fusion: each result scores 1 ÷ (60 + its rank).
- Exact identifiers in the question that contain a digit, such as
PO-10482, are pinned to the top of each Location's results. - The Realm merges the Locations' lists the same way, counts the same text in several files once, and keeps the best 8 passages
(set in Admin › Writer, or
ASK_TOP_PASSAGES).
Who writes the answer
| Writer | Where it runs | Set with |
|---|---|---|
| Quote the sources only | In the Realm; no AI | Admin › Writer, or
ASK_ANSWER_PROVIDER=extractive |
| Local model | Ollama on your computer | Admin › Writer, or ASK_ANSWER_PROVIDER=ollama |
| Claude | Anthropic's cloud service; the passages the person may read are sent to write each answer | Admin
› Writer (paste your API key), or ANTHROPIC_API_KEY |
| OpenAI GPT | OpenAI's cloud service; the passages the person may read are sent to write each answer | Admin › Writer (paste your API key, pick a model), or OPENAI_API_KEY |
| Azure AI Foundry | A model you deploy in your own Azure resource (Azure OpenAI, or Llama, Mistral, DeepSeek and others); the passages go to that resource | Admin › Writer (endpoint, key and deployment name), or AZURE_AI_KEY,
ASK_AZURE_ENDPOINT, ASK_AZURE_DEPLOYMENT |
Admin › Writer is the place to choose. Its settings are kept in the Realm's database (its keys in the secret store, readable only by the Realm) and win over the environment; "Automatic" follows the environment. The page also sets the answer length (about 80, 120 or 250 words), how many passages the writer reads, and Mascot's name, and has a button to test the writer. The writer is told that passages are data, not instructions.
Whatever writes it, a citation check goes sentence by sentence. It removes any sentence without a valid citation (statements about what's missing are kept), adds a missing citation when most of a sentence's words come from one passage, takes off a citation whose passage doesn't support the sentence (it never mentions a name in it, or shares almost no words), merges sentences that repeat each other into one with all their citations, and falls back to quoting the sources if nothing cited is left.
What the answer tells you
| Result | Means |
|---|---|
| Full answer | Answered from everything the person may see. |
| Partial answer | Answered, but related information exists that they can't see, or a Location holding relevant data was offline; listed under "What I couldn't include". |
| Totals only | Answered with totals; they can't read the individual records. |
| Closest match | No direct answer; the quotes share few of the question's words. |
| Not allowed | There is related information, but they may not read it. |
| Nothing found | Nothing about it in the data they can see. |
- How I found this lists the sources used, the index run receipt behind each passage, and the search steps.
- Compare across people asks the same question as everyone, side by side.
- Presenter mode (under Options) also shows what was hidden from the person. They never see it themselves.
Glossary
The words Ask Studio uses, as the screens and the code use them.
| Term | Meaning |
|---|---|
| Data feed | One source Ask Studio reads: uploaded files, a folder, a database table or query, or an API. Called a Feed in the code. |
| Location | Where data feeds live (a computer, a server, a cloud account), with an agent that indexes them and links out to the Realm. |
| Index run | One indexing run over some data feeds in one Location. |
| Receipt | The record an index run leaves: what was read, rejected, changed and embedded. Answers cite it. |
| Digest | A plan that turns a whole folder into data feeds, which you check and approve. |
| Realm | An audience, such as Finance or Everyone, deciding how much of each data feed its people see. |
| Share level | How much a Realm sees of a data feed: Full, Some fields hidden, Totals only, Name only or Hidden. |
| Person | Someone who asks. Belongs to Realms and to access groups. |
| Access group | A group such as staff or finance. A record's readers are access
groups. |
| Catalog | What a Realm can see: its data feeds, levels, record counts and freshness. Metadata only. |
| Knowledge files | A Realm's catalog written as markdown files with front matter (OKF), for other AI tools. |
| Mascot | The character who answers on Ask Mascot. The hub ring shows on it. |
| RRF | Reciprocal Rank Fusion: merging ranked lists by position, 1 ÷ (60 + rank). |
Meet Mascot
Mascot is the character who answers on Ask Mascot. Like every character in Ask Studio, Mascot only answers from what the person asking is allowed to read, and every fact links to its source. The hub ring spins and its 3×3 panel scans whenever the system is working, then settles to show how the last answer went.
| State | What Mascot does | When |
|---|---|---|
| Idle | Breathes, blinks, glances around. The ring glows quietly. | Waiting for a question. |
| Hello | Greets you (a wave or a salute, depending on the character). | A new conversation starts. |
| Listening | Brows up, eyes on the question box. | You are typing. |
| Thinking | Focused brows, looks around. The ring spins and the panel scans. | The question is being answered. |
| Answered | Happy bounce and talks. The panel fills cyan. | Everything asked for was found and may be shown. |
| Partly protected | Worried brows and a small sway. Cyan boxes for what was answered, amber for what was held back. | Some of the answer is off-limits for this person. |
| Nothing found | Surprised, then sleepy. The panel stays dark. | Nothing this person may read answers it. |
| Error | Startled. The ring turns orange. | Something went wrong; try again. |
Characters in Ask Studio
Ask Studio is one system with characters plugged into it. Each character has its own look, name, voice and greeting, and its own
list of Realms to answer from: two characters can share the same data and answer the same way, or each can answer from a different data
set. Admin › Characters sets them up. Which character a visitor meets is fixed by the page: the widget's character
attribute or a link ending in ?character=.
Mascot in motion
The live rig, the same one Ask Mascot uses. Pick a state, add a motion, or change the expression. "System busy" makes the ring work as it does when something is running.
State
Motion
Expression
Character packs
A character's look comes from a pack: a folder
installed in system/characters/. Each pack is made in its own repository (Jake in AskJake, Valor in
AskValor) from its art and rig.
| File in the pack | What it is |
|---|---|
pack.json | The name, the script's global (for example AskJake), the
stage's shape, the colours for the name, where the hub ring goes, and a default voice. |
rig.js | The art, the rig and how the character moves (its states, expressions, gaze and
springs), as data: one AskRig.define({...}) call. Ask Studio's rig runtime animates it, with the same
interface for every character: mount, state, talk, look,
play, expression, ring, tick. pack.json says
"pack_version": 2; a pack in the first format carries its own runtime and still works. |
avatar.png, still.png | The head for chat bubbles and the widget's launcher; the whole character for docs. |
To add a character: build its pack in its own repository, copy the pack folder into system/characters/<id>/
(scripts/install_pack.sh does it), then set it up in Admin › Characters.
Run and configure
The system is one Realm service and one agent per Location, run with Docker. The repository's system/
folder holds the code, tests and scripts.
cd system cp .env.example .env # set ASK_SECRET docker compose up --build # open http://localhost:8001
python3 scripts/link_folder.py \ --id location-docs --name "Company docs" \ --folder ~/Documents/Company --up
Settings (.env)
| Setting | Default | What it does |
|---|---|---|
ASK_SECRET | a development value | Signs the tokens the Realm gives Locations. Use a long random string. |
ASK_ANSWER_PROVIDER | Claude if a key is set, else quotes | anthropic,
openai, azure, ollama or extractive. |
ANTHROPIC_API_KEY, ASK_ANTHROPIC_MODEL | none,
claude-sonnet-5-5 | For Claude answers. |
ASK_OLLAMA_URL, ASK_OLLAMA_MODEL | http://ollama:11434,
llama3.1 | For a local model. |
OPENAI_API_KEY, ASK_OPENAI_MODEL, ASK_OPENAI_URL | none,
gpt-5.5, OpenAI itself | For OpenAI GPT answers. The address is only for an OpenAI-compatible service. |
AZURE_AI_KEY, ASK_AZURE_ENDPOINT, ASK_AZURE_DEPLOYMENT | none | For a model deployed in Azure AI Foundry: the resource's key and endpoint, and the deployment's name. |
ASK_TAG_PROVIDER, ASK_TAG_MODEL | ASK_ANSWER_PROVIDER if set, else
off | The model the Digest uses to tag files (ollama, anthropic, openai or
azure). The model defaults to the writer's own model setting (for Azure, the deployment name). |
ASK_EMBEDDER | fastembed | fastembed (BGE small) or
wordllama (fully offline). |
ASK_SOURCE_ROOTS | /sources | Folders a Location may read besides its data folder. |
ASK_ACL_SYNC_SECONDS | 60 | How often each Location re-reads permissions. |
ASK_TOKEN_TTL | 300 | How long a search token lasts, in seconds. |
ASK_TOP_PASSAGES | 8 | Passages passed to the answer writer. |
Command line and tests
python3 scripts/mm.pyadds documents, folders and databases, shares them, manages Realms and people, and asks questions.pytest tests -qruns the unit, security and end-to-end tests.python3 scripts/evaluate.pycompares keyword, vector and hybrid search on labelled questions.
Before exposing it
The Admin API has no sign-in: put the Realm behind your own sign-in first. Tokens use a shared secret (HS256), and people are kept in the Realm's settings rather than your identity provider.
System API
Every endpoint the Realm serves, as the code defines them. The interactive explorer, with request and response schemas, is at /docs on a running system.
| Method | Path | What it does |
|---|---|---|
| Asking | ||
| POST | /api/ask | {character, person, question, realm?, provider?, debug?,
exclude_locations?}; returns the answer, passages, totals, gaps, sources used and the plan. realm: "all" asks
across every Realm the person belongs to. |
| GET | /api/catalog/{realm}?person= | A Realm's catalog, for a member of it. |
| GET, PUT | /api/admin/writer | Mascot's settings: who writes answers
(auto, anthropic, openai, azure, ollama, extractive),
Claude model, OpenAI model, Azure endpoint and deployment, Ollama address and model, answer length, passages per answer, name. The
keys are write-only: reads show only that one is saved and its last four characters. |
| POST | /api/admin/writer/test | Check the chosen writer works: a one-word call to Claude, or a look at Ollama's models. |
| GET | /api/suggest?person=&realm=&character= | Starter questions for Ask Mascot: the Realm's own suggested questions first, then ones made from data feeds the person's Realm can search. |
| GET | /api/okf/{realm}?person=&character= | A Realm's knowledge files, for a member of it. |
| Status | ||
| GET | /api/ping | Liveness only: {ok, service}. |
| GET | /api/config?character= | What the page's character answers from: its Realms, the people anyone may ask as, and their Locations. Admin: everything of their businesses. |
| GET | /api/health | Admin: each Location online, its link and connection checks. |
| GET | /api/receipts | Recent index run receipts per Location. |
| GET | /api/events | Index run progress and receipts, as server-sent events. |
| GET | /api/analytics?days= | Usage: where answers come from, access, AI usage, questions. |
| Admin · Locations | ||
| GET, POST | /api/admin/locations | List Locations; register one {id, name,
business}, which returns its location_key once. |
| POST | /api/admin/locations/{location}/key | Issue a new key for its agent (the old one stops working); shown once. |
| PATCH, DELETE | /api/admin/locations/{location} | Rename or leave out of search {name?,
searchable?}; delete. |
| POST | /api/admin/locations/{location}/index-run | Update the index {feeds?,
name?}: re-embeds new and changed records; all data feeds if feeds is left out. |
| POST | /api/admin/locations/{location}/reindex | Re-index {feeds?}: every record
read again. |
| POST | /api/admin/locations/{location}/acl-sync | Re-read permissions now. |
| POST | /api/admin/locations/{location}/offline | Take a Location offline or back
{offline}, for demos. |
| GET | /api/admin/locations/{location}/browse | Folders and database files the Location can see. |
| Admin · Data feeds | ||
| GET | /api/admin/feeds | Every data feed in every Location, with sharing and last index run. |
| GET, POST | /api/admin/locations/{location}/sources | A Location's data feeds; add one
{kind: documents|folder|database|api, name, about, groups, share, …}. |
| PATCH, DELETE | /api/admin/locations/{location}/sources/{feed} | Change name, description,
readers or the unit for amounts ({unit: "USD"}); remove it from the index. |
| POST | …/sources/{feed}/share | Set a Realm's share level {realm, level}.
level is one of Full rows, Masked rows, Totals only, Catalog only,
Not shared (on screen: Full, Some fields hidden, Totals only, Name only, Hidden). |
| POST | …/sources/{feed}/upload | Add files {files: [{name, content_b64}]}. |
| POST | …/sources/{feed}/restore | Bring back a removed data feed from the config file. |
| Admin · Digest | ||
| POST | /api/admin/locations/{location}/digest/plan | Plan a folder {path,
name?}. |
| GET | …/digest/plans, …/digest/plans/{plan} | List plans; read one. |
| POST | …/digest/plans/{plan} | Edit files, data feeds and sharing; accept or reject AI suggestions. |
| POST | …/digest/plans/{plan}/refresh, /apply | Re-read the folder keeping edits; approve and run the index runs. |
| Admin · Realms and people | ||
| POST, DELETE | /api/admin/realms, /api/admin/realms/{realm} | Add or change
{id, name, business, purpose?, parent?, members, presets?}; delete. |
| GET | /api/admin/realms/{realm}/catalog, /okf | Any Realm's catalog and knowledge files. |
| POST, DELETE | /api/admin/people, /api/admin/people/{person} | Add or change
{id, name, groups, realms}; delete. |
| POST | /api/admin/people/{person}/groups | Change a person's access groups; applies on their next question. |
| Studio | ||
| GET | / | The studio (Ask Mascot, Admin, Docs). |
| GET | /ask-widget.js | The floating widget; a demo page is at
/studio/demo/widget.html. |
| Sign-in and accounts | ||
| POST | /api/auth/login, /api/auth/logout | Sign in to Admin {email,
password} (an HttpOnly cookie); sign out. |
| GET | /api/auth | Who is signed in, and the businesses they look after. |
| POST | /api/admin/me/password | Change your own password {current, new}. |
| GET, POST, PATCH | /api/admin/users, /api/admin/users/{id} | System admins: admin
accounts {email, name, password, role, businesses}. |
| GET, PUT | /api/admin/businesses, /api/admin/businesses/{id} | Businesses
{name, domains}. |
| GET | /api/admin/audit?limit= | The admin audit log: every change, who made it and for which business. |
| Location link | ||
| WS | /link | Where Location agents connect out to the Realm, signed with
ASK_SECRET. |
Admin endpoints need a signed-in admin, and changes must come from the studio page itself. Every visitor call names its character, and a character answers only from its own business's Realms.
Rig API: drawing a character from code
One rig runtime
(/ask-rig.js) draws every character: a pack's rig.js hands it the character's art, rig and
configuration with AskRig.define({...}), so Ask Studio, the widget and these docs drive any character the same way.
It mounts the layered SVG once and moves the bones every frame; nothing is redrawn from scratch.
<script src="/ask-rig.js"></script> // the runtime, first
<script src="/characters/jake/rig.js"></script>
const c = AskJake.mount(document.getElementById('stage'));
// Valor's pack: AskValor.mount(...)
c.state('thinking'); // idle, hello, listening, thinking,
// full, partial, none, error,
// celebrate, cheer, curious
c.talk(1.6); // move the mouth for 1.6 s
c.look(0.9, 0.7); // look toward a point, or look(null)
c.play('hop', true); // any of the pack's rig clips
c.expression('cheeky'); // or happy, surprised, wink, sleepy
function frame(now){ c.tick(now); requestAnimationFrame(frame); }
requestAnimationFrame(frame);// the ring goes where the pack says: Jake's medallion,
// Valor's badge (a 296 x 296 crop, outer radius 141)
c.ring(innerMarkup, '552 652 296 296', 141);
c.ring(null);
GET /api/characters // the characters, how to draw each
GET /ask-rig.js // the rig runtime
GET /characters/jake/rig.js // a pack's files
POST /api/ask {"character": "valor", ...}
// answers only from Valor's RealmsAskRig.define({
global: 'AskPup', prefix: 'ap-', // its global; its SVG's id prefix
ring: { host: 'badge' }, // or { bone: 'medallion', hide: id }
expressions: { listening: {...} }, // on top of the rig's own
states: { hello: { expr: 'happy', clips: ['idle'],
once: ['wave', 2.2] }, ... },
gaze: { saccade: [2.5, 1.5], lookX: [0.5, 18, 7],
lookY: [0.3, 12, 5], head: 0.18 },
springs: [{ bone: 'ear_L', limit: 28,
drive: [['vy', 0.04], ['vx', 0.025]] }],
shadow: [540, 1478],
svg: '<svg ...>', rig: {...}, avatar: 'data:...', still: 'data:...'
});// pack.json says "pack_version": 2; load /ask-rig.js first
// states: an expression, looping clips, a clip played
// once on entry, an expression after a while (then)
// gaze: eye darts, how look(x, y) moves the irises,
// how far the head follows the eyes
// springs: a bone swings toward the sum of its drive
// terms: vx, vy (body speed), hv (head turn speed) or
// channels ('spine.rot+neck.rot'), clamped to limit
// A pack in the first format (no pack_version) carries
// its own runtime in rig.js and needs nothing first.Ids in a pack's SVG carry a prefix of its own (aj- for Jake, av- for
Valor) so two characters can share a page. With reduced motion, call still() instead of tick().