wawesome 0.14.2 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +127 -59
  2. package/dist/index.mjs +1224 -949
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -312,31 +312,38 @@ stderr, so a redirect catches the secret and not one character besides:
312
312
  npx wawesome credentials mint ci-pipeline > secret.txt
313
313
  ```
314
314
 
315
- A credential carries `deploy`, reaches every App in the workspace, and expires in ninety days unless
316
- you say otherwise:
315
+ A capability is one cell of a grid: a verb, `read` or `write`, and the resource it acts on. A
316
+ credential carries the words a deploy needs, reaches every App in the workspace, and expires in
317
+ ninety days unless you say otherwise:
317
318
 
318
319
  ```bash
319
- # reads and runs what somebody else shipped, restricted to one App, never expires
320
- npx wawesome credentials mint agent -c read,logs:read,invoke -a prod --expires never
320
+ # reads what ran and fires a run of what somebody else shipped, on one App, never expires
321
+ npx wawesome credentials mint runner -c read:apps,read:functions,read:invocations,write:runs -a prod --expires never
321
322
 
322
- # ships code and attaches the domain the project declares
323
- npx wawesome credentials mint ci -c deploy,domains:attach -a prod
323
+ # ships code into an App it may create, and attaches the domain the project declares
324
+ npx wawesome credentials mint ci -c write:apps,write:functions,write:env,read:tenant,write:domains -a prod
324
325
  ```
325
326
 
326
327
  | Flag | Meaning |
327
328
  |:---------------------|:------------------------------------------------------------------------------|
328
- | `-c, --capability` | `deploy`, `read`, `logs:read`, `invoke`, `domains:attach`, or any mix. Repeatable or comma-separated. Default `deploy` |
329
+ | `-c, --capability` | A `{read\|write}:{resource}` word — `read:apps`, `write:functions`, `write:env`, `write:runs` and the rest. Repeatable or comma-separated. Default: `write:apps`, `write:functions`, `write:env`, `read:domains`, `read:tenant` |
330
+ | `--preset` | The words one job needs, by the job's name: `viewer`, `deployer`, `member`. [Which credential for which job](#which-credential-for-which-job) is what each one grants. Not with `-c` |
329
331
  | `-a, --app` | Restrict to these Apps, by slug. Repeatable. Default: every App in the workspace |
330
332
  | `--expires` | Days, or `never`. Default: 90 |
331
333
 
332
- `deploy` reaches the reads of what it writes, so a credential minted with it carries `read` as well:
333
- it lists the workspace, the Apps in it, the Functions in an App and their versions. The command
334
- prints the words the credential ended up with.
334
+ The resources are `tenant`, `apps`, `functions`, `env`, `domains`, `invocations`, `runs`,
335
+ `schedules`, `previews` and `egress`. A write reaches the read of the same resource, so
336
+ `write:functions` carries `read:functions` and there is no reason to name both. The command prints
337
+ the words the credential ended up with.
338
+
339
+ Some things are a person's alone, whatever a credential carries: renaming the workspace, detaching a
340
+ domain, widening the egress allowlist, billing, and this workspace's own credentials and members.
341
+ Those are not words to mint — a credential asking for one is told a person has to be at the keyboard.
335
342
 
336
343
  An App named in a restriction does not have to exist yet. A pipeline whose first deploy creates the
337
344
  App it was minted for is the ordinary case. A restriction bounds what a credential reads as well as
338
- what it disturbs, and `deploy` is the hole in it: a Function deployed into an App the credential does
339
- reach still reads the whole workspace's environment at runtime.
345
+ what it disturbs, and `write:functions` is the hole in it: a Function deployed into an App the
346
+ credential does reach still reads the whole workspace's environment at runtime.
340
347
 
341
348
  ### List them
342
349
 
@@ -470,21 +477,32 @@ concluding the platform cannot do it.
470
477
 
471
478
  | Tool | What it does | Needs |
472
479
  |:---------------------------|:------------------------------------------------------------------------|:--------------|
480
+ | `whoami` | The workspace, where it is administered, the words this credential carries, the Apps it reaches | nothing |
473
481
  | `list_templates` | The template catalogue, with what each one is for | nothing |
474
482
  | `get_template` | One template's files, environment contract and outbound providers | nothing |
475
- | `list_apps` | The Apps the credential reaches, with the slug each is addressed by | `read` |
476
- | `list_functions` | The Functions in one App, and what their live version carries | `read` |
477
- | `list_versions` | A Function's Versions, and which one is live | `read` |
478
- | `get_usage` | The Tenant's usage and headroom | `read` |
479
- | `list_invocations` | Recent Invocations of a Function, failures included | `logs:read` |
480
- | `get_invocation` | One Invocation by id | `logs:read` |
481
- | `read_invocation_logs` | An Invocation's log body, a page at a time | `logs:read` |
482
- | `diagnose_latest_failure` | The latest failure, its logs and the Version that ran it, in one call | `logs:read`, `read` |
483
- | `deploy_function` | Deploys code and the static files it carries | `deploy` |
484
- | `deploy_status` | How a deploy went, by the handle it answered with | `deploy` |
485
- | `rollback_function` | Puts a Version the Function already has back on its public address | `deploy` |
486
- | `set_env_var` | Sets an App's environment variable, read at the next invocation | `deploy` |
487
- | `invoke_function` | Runs a deployed Function once and answers with the Invocation id | `invoke` |
483
+ | `list_apps` | The Apps the credential reaches, each with its address and its dashboard page | `read:apps` |
484
+ | `list_functions` | The Functions in one App, and what their live version carries | `read:functions` |
485
+ | `list_versions` | A Function's Versions, and which one is live | `read:functions` |
486
+ | `get_function_source` | The code a Function is running, and the static files it carries | `read:functions` |
487
+ | `get_usage` | The Tenant's usage and headroom | `read:tenant` |
488
+ | `list_invocations` | Recent Invocations of a Function, failures included | `read:invocations` |
489
+ | `get_invocation` | One Invocation by id | `read:invocations` |
490
+ | `read_invocation_logs` | An Invocation's log body, a page at a time | `read:invocations` |
491
+ | `diagnose_latest_failure` | The latest failure, its logs and the Version that ran it, in one call | `read:invocations`, `read:functions` |
492
+ | `deploy_function` | Deploys code and the static files it carries | `write:functions` |
493
+ | `deploy_status` | How a deploy went, by the handle it answered with | `read:functions` |
494
+ | `rollback_function` | Puts a Version the Function already has back on its public address | `write:functions` |
495
+ | `set_env_var` | Sets an App's environment variable, read at the next invocation | `write:env` |
496
+ | `invoke_function` | Runs a deployed Function once and answers with the Invocation id | `write:runs` |
497
+ | `fetch_function` | Fetches one path from a Function's live Version and answers what it served | `write:runs` |
498
+
499
+ A word that writes reaches the read of the same thing, so `write:functions` needs no `read:functions`
500
+ beside it, and the presets below are named in exactly these words.
501
+
502
+ `whoami` is the one to call first. It answers the workspace, every word the credential carries and
503
+ the Apps it reaches, and it asks for nothing — so your agent can say what it is able to do before it
504
+ offers, rather than finding the edge by being refused in front of you. A member's session calling the
505
+ endpoint gets the same answer for the role they hold.
488
506
 
489
507
  A deploy is two calls. The first says which of your files the platform does not already hold, by
490
508
  content hash; your agent uploads those bytes to `PUT /v1/assets/{content_hash}` with the same
@@ -503,32 +521,43 @@ is all the platform permits, and an agent taking it would end yours.
503
521
  ### Which credential for which job
504
522
 
505
523
  A credential carries the words its job needs and stops there. They are the same words
506
- [Deploy credentials](#mint-one) mints with.
524
+ [Deploy credentials](#mint-one) mints with, and `--preset` fills them in for you: it expands into the
525
+ words below before the request is built, and the command prints the words the credential ended up
526
+ with. A preset and `-c` together are refused rather than merged, since both name the same thing. The
527
+ preset is the name of the job; the words are the grant, and they are what travels.
528
+
529
+ A preset is a workspace role, and the ones offered here are the roles carrying nothing only a person
530
+ can hold. The name a job is minted under and the standing a member is given are one thing, rather
531
+ than two lists somebody has to keep level.
507
532
 
508
- **Read and investigate, without the ability to ship.** The agent enumerates your Apps, reads
509
- Functions, Versions, Invocations and log bodies, diagnoses a failure, checks usage, and browses
510
- templates. Every tool that ships code or runs it refuses:
533
+ **Read and investigate, without the ability to ship** — `viewer`, which is every `read:` word the
534
+ platform has. The agent enumerates your Apps, reads Functions, Versions, Invocations and log bodies,
535
+ diagnoses a failure, checks usage, and browses templates. Every tool that ships code or runs it
536
+ refuses:
511
537
 
512
538
  ```bash
513
- npx wawesome credentials mint investigator -c read,logs:read -a prod
539
+ npx wawesome credentials mint reader --preset viewer -a prod
514
540
  ```
515
541
 
516
- **Read, investigate and run.** The same, plus `invoke_function`, so the agent can exercise a Function
517
- and read what it did. It still cannot deploy, roll back, or set a variable:
542
+ **Ship and read back** — `deployer`, which is every read plus `write:apps`, `write:functions`,
543
+ `write:env` and `write:runs`. Deploy, run, diagnose, roll back, set a key. Every tool in the table:
518
544
 
519
545
  ```bash
520
- npx wawesome credentials mint runner -c read,logs:read,invoke -a prod
546
+ npx wawesome credentials mint ci --preset deployer -a prod
521
547
  ```
522
548
 
523
- **The whole loop.** Deploy, run, diagnose, roll back, set a key. Every tool in the table:
549
+ **The whole workspace** — `member`, which is the deployer's words plus `write:schedules` and
550
+ `write:previews`, for an agent that pauses a schedule or cuts a preview as well:
524
551
 
525
552
  ```bash
526
- npx wawesome credentials mint agent -c deploy,read,logs:read,invoke -a prod
553
+ npx wawesome credentials mint agent --preset member -a prod
527
554
  ```
528
555
 
529
- Add `domains:attach` where the project declares a custom domain the deploy should claim. Nothing
530
- gives an agent billing, plan changes, credential management or your workspace's slug: those are
531
- closed to every credential, whatever it carries.
556
+ Add `write:domains` where the project declares a custom domain the deploy should claim. Nothing gives
557
+ an agent billing, a plan change, credential management or the ability to rename your workspace: those
558
+ are closed to every credential, whatever it carries. Reading the workspace's name and address is not
559
+ among them — `whoami` answers both to any credential, which is how the agent knows where what it
560
+ deploys will appear.
532
561
 
533
562
  Keep `-a` on all three. A credential restricted to one App is refused on every tool that names
534
563
  another, which is what stops an agent working on one client's project from touching another's.
@@ -543,10 +572,18 @@ A refusal is the tool's answer, not a protocol error, and it carries the same `r
543
572
  prints:
544
573
 
545
574
  - `credential-missing-capability`. The credential does not carry a word this tool needs. The refusal
546
- names it in `missing_capability`, which is the word to mint a credential with.
575
+ names the first in `missing_capability` and every one of them in `missing_capabilities`, which are
576
+ the words to mint a credential with. This one answers `403` rather than `200`, with a
577
+ `WWW-Authenticate: Bearer error="insufficient_scope"` challenge naming the same words and where
578
+ this endpoint's OAuth metadata is — a connector installed through consent reads it and asks its
579
+ human to widen the grant rather than to install again.
547
580
  - `app-outside-credential-scope`. The credential is restricted to named Apps and this is not one of
548
581
  them. A wider capability changes nothing here.
549
582
 
583
+ A `401` is the credential itself: revoked, expired, or one this platform never issued. Nothing else
584
+ answers one — a workspace that cannot deploy for a lapsed plan or a restriction is refused under its
585
+ own reason, and re-installing the connector would not fix either.
586
+
550
587
  A quota refusal carries its numbers under `allowance` beside the prose, so your agent reports
551
588
  "3 of 3 App slots" rather than handing you a sentence to read.
552
589
 
@@ -575,6 +612,33 @@ URL, serving the code its callers already hold. The CLI remembers where this dir
575
612
  deployed and asks before that happens, naming both URLs. If you meant it, delete the old Function
576
613
  from the dashboard once nothing calls it.
577
614
 
615
+ ### A site with no handler
616
+
617
+ Leave `entry` out, write no `src/index.ts`, and point `"assets"` at the directory your build wrote
618
+ its pages into:
619
+
620
+ ```json
621
+ {
622
+ "app": "my-app",
623
+ "function": "root",
624
+ "assets": "dist"
625
+ }
626
+ ```
627
+
628
+ That deploys the pages and no code at all, and the site is live at your App's address. Nothing in the
629
+ file declares a kind: a project with a handler is one that has an entry point, and a project with
630
+ both deploys them together and rolls them back together.
631
+
632
+ An `entry` you did name and that is not there is a hard error, so a typo never quietly becomes a site
633
+ with nothing running behind it. Every deploy says which of the two it landed.
634
+
635
+ One thing the deploy will warn you about: everything beneath a Function's address reaches that
636
+ Function, so a page your root Function carries at `about/index.html` is shadowed by a sibling Function
637
+ named `about`. The deploy says so and names both addresses. It refuses nothing — the two Functions are
638
+ versioned independently, and either one may deploy first.
639
+
640
+ ### Static files
641
+
578
642
  Add `"assets"` to deploy static files beside your code:
579
643
 
580
644
  ```json
@@ -596,28 +660,32 @@ Files are served straight from object storage; your Function is never invoked fo
596
660
  invocation is recorded. They answer on your App's own hostname and nowhere else. On the
597
661
  development path form (`/x/<tenant>/<app>/<function>/...`) the same address reaches your handler
598
662
  as it always has, because a file on an origin every workspace shares would be same-origin with
599
- all of them. Each carries `Cache-Control: public, max-age=31536000, immutable` and an
600
- `ETag`, so name your build output by content hash. A file's bytes must never change under a name a
601
- browser has already cached for a year. The content type comes from the extension against a fixed
602
- allowlist and is never sniffed; anything off it is served as a download.
663
+ all of them. A file beneath `assets/` carries `Cache-Control: public, max-age=31536000, immutable`,
664
+ so name your build output by content hash: its bytes must never change under a name a browser has
665
+ already cached for a year. A file outside `assets/` keeps one name across every deploy, so it carries
666
+ `max-age=300` instead — five minutes is inside the reach of a deploy and of a rollback, which a year
667
+ is not. The content type comes from the extension against a fixed allowlist and is never sniffed;
668
+ anything off it is served as a download.
603
669
 
604
670
  Two rules to know about:
605
671
 
606
672
  - **Everything beneath `assets/` is static**, whatever the deploy carries. A request there never
607
673
  reaches your handler, and an unknown path under it is a 404 rather than a route for you to answer.
608
- - **At most 100 files may sit outside `assets/`.** Those paths travel on the version record so a
609
- request can be routed without a lookup per file. Put bulk output under `assets/`, where a file
610
- costs nothing; `favicon.ico`, `robots.txt` and a `.well-known/` directory are what the rest is
611
- for.
612
-
613
- **HTML is refused at deploy time.** Your Function renders its own markup, and a document served from
614
- your App's own origin is the sharpest same-origin risk a static file carries.
615
-
616
- **An SVG is served inert.** The rule behind the refusal above is that nothing you deploy as a file
617
- becomes a page on your App's own origin, and an SVG opened directly in a browser would become one.
618
- It runs script, and it renders whatever HTML a `<foreignObject>` holds. It is served rather than
619
- refused because making it inert costs the file nothing. An `<img src="logo.svg">` never ran that
620
- script and is never checked against the policy, so your drawings render as they always did. Every
674
+ - **A file outside `assets/` answers at its own name**, however many of them a deploy carries.
675
+ `favicon.ico`, `robots.txt` and a `.well-known/` directory are what that is for; hashed build
676
+ output belongs under `assets/`.
677
+
678
+ **A page is a file like any other.** An `index.html` your build wrote is deployed, hashed, retained
679
+ and billed exactly as your other files are, and it is what a directory-style address resolves to: a
680
+ path whose last segment carries no extension is served the `index.html` beneath it, so `/`, `/about`,
681
+ `/about/` and `/blog/hello` each answer with a page. A page revalidates rather than being pinned for a
682
+ year, wherever it sits, so deploying and refreshing is a loop that works.
683
+
684
+ **An SVG is served inert.** Nothing you deploy as a resource should be able to run script on your
685
+ App's own origin, and an SVG opened directly in a browser can: it runs script, and it renders whatever
686
+ HTML a `<foreignObject>` holds. It is served rather than refused because making it inert costs the
687
+ file nothing. An `<img src="logo.svg">` never ran that script and is never checked against the
688
+ policy, so your drawings render as they always did. Every
621
689
  SVG and XML file carries `Content-Security-Policy: script-src 'none'; sandbox`, and only navigating
622
690
  straight to one loses anything. Such a file is sandboxed onto an origin of its own, so its links no
623
691
  longer navigate and a page embedding it through `<object>` or `<iframe>` cannot reach into its DOM.
@@ -732,9 +800,9 @@ npx wawesome domains
732
800
  ```
733
801
 
734
802
  It prints the domain, where it got to, when the platform last checked, and the records to add. It
735
- changes nothing, and a deploy credential carrying `deploy` can run it, so a pipeline reads the same
736
- thing you do. The TXT record is minted a moment after the name is claimed, and an attach that outran
737
- it prints a row saying so rather than dropping the record from the list. That row is what this
803
+ changes nothing, and a deploy credential carrying `read:domains` can run it, so a pipeline reads the
804
+ same thing you do. The TXT record is minted a moment after the name is claimed, and an attach that
805
+ outran it prints a row saying so rather than dropping the record from the list. That row is what this
738
806
  command fills in, and it is where the deploy sends you: deploying again would change nothing, and a
739
807
  deploy that changes nothing is refused.
740
808