Keys and spaces
One key for your app, one memory for each of its users, and nothing reaches across.
Keys
Make a key under API keys in the Geniffy app, and name it after where it will run: Production app,
Staging, Laptop. It is shown once, because Geniffy keeps only a hash of it, so put it straight into an
environment variable. Every key starts gnf_live_.
export GENIFFY_API_KEY="gnf_live_..."A key reaches your own memory and every space beneath it, which is what lets one key serve a whole app.
- Keep it on your server. Never put it in a web page or a mobile app, where anyone can read it out. Whoever holds it can read every memory it reaches.
- Use one key for each app and each environment. Requests in the Geniffy app shows which key
made every call, API keys shows when each was last used, and you can revoke one without stopping
the others. Keys tell callers apart, not data: every key you make reaches the same memories, so keep
test data in spaces of its own, such as
test_user_1. - To rotate a key, make a new one, put it where the old one was, then revoke the old one.
- Revoking stops a key within seconds. What was added with it stays; only the key stops working.
Spaces
A key alone reaches one memory, yours. That is all a script needs, and not enough to build an app on: every one of your users would land in the same pile. A space is your own name for one of your users, and each space is a memory of its own that no other space can read.
Naming one
A space is named by your code and exists from the first time your code writes to it. There is nothing to create first.
mem = client.space(f"user_{user.id}") # bind a client to one user, per request
mem.memories.add("Prefers WhatsApp, never email.")
mem.context("How do they like to be contacted?")const mem = client.space(`user_${user.id}`);
await mem.memories.add("Prefers WhatsApp, never email.");
await mem.context("How do they like to be contacted?");Over HTTP, send the space in the X-Geniffy-Space header, or as space in the body or the query string.
What a request spells out beats the header, and an empty space means your own memory even when a header
names one.
A space name is up to 128 letters, digits, dots, dashes or underscores, and starts with a letter or a
digit: customer_1042, user.8841, team-billing. Anything else is refused with bad_space, before it
reaches any memory.
What a space promises
- A space reads only its own memories. Another space cannot, and neither can your own memory.
- Your key reaches every space beneath it, so one key serves your whole app.
- Erasing a space takes everything held for that user, and only that.
Listing and erasing
client.spaces() # which spaces hold anything, most recently written first
client.forget_space("user_8841") # everything held for that user, goneawait client.spaces();
await client.forgetSpace("user_8841");forget_space is the call to make when one of your users asks to be forgotten. It cannot be undone.
You can also find any space, see what is held for it, and erase it under Spaces in the Geniffy app.
The one mistake to avoid
If your app serves more than one person and a call names no space, it writes to your own memory. Nothing refuses it, because using the API for yourself is a perfectly good thing to do. But every one of your users then ends up in one memory, and your bot can tell one customer about another.
Bind a client to a space at the edge of your request handling, and use only the bound client below it. The Spaces page in the Geniffy app warns you when memories arrive through the API with no space named.