.env, keep the key in your app’s database, sealed, and change it with one Ace command:
no redeploy, no edit to the environment. That suits an app where the key is rotated now and then, or
where the person who holds the key isn’t the person who deploys.
Sealed under APP_KEY
AdonisJS’s encryption service seals each value, tied to this table, so a value moved to another
column won’t open.
Stored by Ace
node ace attract:store-key asks for the key, checks the site accepts it, then stores it.Same calls
The package builds the client from the row;
@inject() and every call stay as they were.Set up Lucid
The table lives in your app’s database. A starter kit has Lucid already; otherwise:
Terminal
Configure the package for the database
Terminal
config/attract.ts with sitesFrom: 'database', the attract_api_keys migration, and
the provider and commands in adonisrc.ts. Nothing goes in .env: the key needs only the
APP_KEY every AdonisJS app has.config/attract.ts
Switching an app that already keeps the key in
.env? Add --force, or the command keeps the
config/attract.ts you have (and says so). Once the key is stored, remove the
FETCHSKI_ATTRACT_* lines from .env, .env.example and start/env.ts.Create the table
Terminal
acme in fsk_acme_…), its URL, the token and signing secret
(both sealed), and the key’s signing mode.Store the key
In the site’s CMS, API Keys → New API Key shows the key, its signing secret and (on its
Security tab) its signing mode, once. Keep the page open, then:The key and its secret are asked hidden as you type: leave them out of the command, so they never
land in your shell history. A token-only key isn’t asked for a secret.It builds a client first (so a malformed key or URL fails before anything is sent), makes one call
to prove the site accepts the key, stores it, then runs
Terminal
Terminal
attract:check.
A refused key stores nothing and exits 1.The proving call reads the site’s locales, which any key may do, so the key’s scopes don’t
matter. The site still checks the token, the key’s network allowlist and, for a key that signs
every request, its signature: a refusal of any of them stores nothing. A key that signs only
writes has its secret checked at its first write, since a read is never signed.
SiteClient
needs to know which is the default: set default: 'acme' in config/attract.ts, or take
SiteClients and ask for each by slug (Many sites).
Replace or forget the key
- Replace it
- Forget it
- Change APP_KEY
Run
node ace attract:store-key again with the new key: it replaces the site’s row, so the old key
and secret are gone the moment the new ones are stored (it never opens the old row, so this works
even after APP_KEY changed). A running server keeps the SiteClient it built: restart it to pick
up the new key, or take SiteClients and call forSite() for each job, which reads the table each
time.In tests
Tests fake the client (Testing):fakeAttract() replaces it whether the key is
in .env or the table, so a test needs no row and no key. The package’s own tests check the sealing:
a stored row holds no part of the key in the clear, and reads back whole.
Next: calling the API
Controllers, routes, validation, uploads and errors.