@astram/guide 0.1.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.
@@ -0,0 +1,58 @@
1
+ <!--
2
+ @astram/guide — CAPABILITIES.md (framework-owned, version 0.1.0)
3
+
4
+ What the Astram framework provides THIS version. Refreshed on upgrade (ADR-0025).
5
+ Use this to answer "does the framework already do X?" before building — and, if it
6
+ does NOT, that's a Rule-4 gap (STOP and flag in .astram/guide/gaps.md).
7
+ -->
8
+
9
+ # Astram Framework — Capabilities (v0.1.0)
10
+
11
+ The building blocks the framework provides. Consume these; do not reimplement them.
12
+
13
+ ## Enforcement (`@astram/guard`)
14
+ - **Global deny-by-default guard** — every route is denied unless `@Public()` or
15
+ `@Classified({ typeKey, action })`.
16
+ - **Request authentication** — a `@Classified` route resolves the bearer access token
17
+ → `Principal` → `decide()` automatically. `request.principal` is available to the
18
+ handler (act as the logged-in user).
19
+ - **The ONE seam** — `ShieldModule.forRoot({ auth?, credentialResolver?, decisionAuthority?, config? })`.
20
+
21
+ ## Auth surface (`@astram/guard`, opt-in `auth: true`)
22
+ - `POST /auth/login` `{ identityHint, password }` → `{ session, tokens }` (or an MFA challenge).
23
+ - `POST /auth/mfa` · `POST /auth/refresh` · `POST /auth/logout`.
24
+ - Argon2id password verification, sessions, refresh-token rotation + reuse detection,
25
+ opaque access tokens bound to the principal (server-revocable; logout = immediate kill).
26
+ - MFA/TOTP.
27
+ - You provide: a `CredentialResolver` (reads your users) + a `DecisionAuthority` (your policy).
28
+
29
+ ## The record pipe (`@astram/engine`)
30
+ - **`RecordEngine`** (token `RECORD_ENGINE`) — create/read/update/delete/list over
31
+ registered tables, through one unbypassable pipe: permission gate (`decide()`) →
32
+ server-trusted write-context (anti-forgery) → validate → write (real table) →
33
+ junction sync → audit → event.
34
+ - **Cross-tenant isolation** — a `tenantId`-trusted field auto-scopes reads AND writes
35
+ to the actor's tenant.
36
+ - **Physical wall** — the raw DB pool is sealed in the engine; feature scope never
37
+ receives it. Off-road DB access fails the build (the app's `eslint.config.js`).
38
+ - **Gap signal** — an unsupported field type / operation throws
39
+ `CapabilityNotSupportedError` (a Rule-4 STOP).
40
+ - Field types supported this version: `string`, `number`, `boolean`, `uuid`,
41
+ `timestamp`, `json`. (Anything else → flag a gap.)
42
+
43
+ ## Migrations (`@astram/migrate`)
44
+ - Deploy-time code-table migration runner: timestamped `up()`/`down()` files, a
45
+ `schema_migrations` table, `migrate` / `migrate:revert` / `migrate:status`. Raw SQL
46
+ through a DDL-capable deploy account (the running app never holds DDL).
47
+
48
+ ## Frontend SDK (`@astram/sdk`)
49
+ - Platform-agnostic fact projection for building UIs against the framework.
50
+
51
+ ## NOT provided yet (as of v0.1.0) — these are Rule-4 gaps if you need them
52
+ - Runtime (no-code) object definition / admin-defined objects + runtime DDL.
53
+ - Formula / lookup / rollup computed fields (the pipe's compute stage is a no-op).
54
+ - Business-rule events (the pipe's emit-event stage is a no-op).
55
+ - Extension-field sidecar (runtime custom fields on a registered table).
56
+ - Membership-scoped list filtering (e.g. "companies I belong to").
57
+
58
+ If you need one of these, STOP and flag it in `.astram/guide/gaps.md` (Rule 4).
@@ -0,0 +1,110 @@
1
+ <!--
2
+ @astram/guide — FRAMEWORK_RULES.md (framework-owned, version 0.1.0)
3
+
4
+ This file is the FRAMEWORK layer of the two-layer AI guide (ADR-0025). It is
5
+ installed in node_modules and REPLACED WHOLESALE on `npm update` — never edit it
6
+ in your repo (your edits would be lost on upgrade, and it is not yours to change).
7
+ Put YOUR project's rules in `.astram/guide/` instead; those are never touched by an
8
+ upgrade.
9
+
10
+ These are the framework's HARD RULES — the FLOOR. A project rule in `.astram/guide/`
11
+ may ADD to or TIGHTEN a rule here, but may NEVER weaken or override one. The
12
+ never-touch-core / never-bypass-enforcement rules are non-overridable, full stop.
13
+ -->
14
+
15
+ # Astram Framework — Hard Rules (the floor)
16
+
17
+ You are building features on the **Astram framework** — a sealed security kernel your
18
+ app installs read-only. These rules are non-negotiable. If a project rule in
19
+ `.astram/guide/` appears to relax one of these, the framework rule wins.
20
+
21
+ ## 0. After a framework upgrade, RE-READ this file first
22
+
23
+ The framework evolves. When the app's `@astram/guide` version changes (a framework
24
+ upgrade — `npm update`), this file is replaced with a newer one that may add rules or
25
+ capabilities. **Before continuing feature work after any upgrade, re-read this file
26
+ and `CAPABILITIES.md` from `node_modules/@astram/guide/`** so you are working against
27
+ the current rules, not a stale memory of them. Your project's own rules in
28
+ `.astram/guide/` are unchanged and still apply on top.
29
+
30
+ ## 1. NEVER edit or bypass the framework
31
+
32
+ The framework packages (`@astram/core`, `@astram/guard`, `@astram/engine`,
33
+ `@astram/migrate`, `@astram/sdk`, `@astram/guide`) live in `node_modules` and are
34
+ **immutable**. Do not edit them, patch them, monkey-patch them, or reach around their
35
+ public API. You register your app through the ONE seam — `ShieldModule.forRoot(...)` —
36
+ and consume the exported building blocks. That is the entire contract.
37
+
38
+ - **`@astram/core` is especially sacred.** It is the pure security engine. It has no
39
+ reason to be imported directly by feature code and must never be modified.
40
+
41
+ ## 2. All data access goes through the RecordEngine pipe — never raw DB
42
+
43
+ Feature code must **never** touch the database directly: no `mariadb`/`mysql`/`pg`/
44
+ `typeorm` import, no `DataSource`, no `QueryRunner`, no `SHIELD_DB_POOL`, no
45
+ repository `.save()`/`.query()`. Every create/read/update/delete goes through the
46
+ injected **`RecordEngine`** (`@astram/engine`, token `RECORD_ENGINE`), which applies
47
+ the permission gate, validation, audit, and cross-tenant isolation on every write —
48
+ by construction.
49
+
50
+ This is enforced two ways: the raw DB pool is **sealed inside the engine module** (you
51
+ cannot inject what you are not given), and the app's `eslint.config.js` **fails the
52
+ build** on any off-road DB access under `src/features/**`. If you find yourself
53
+ wanting a raw query, see Rule 4.
54
+
55
+ ## 3. Classify every route (deny-by-default)
56
+
57
+ Every HTTP route is DENIED by the global enforcement guard unless you classify it:
58
+ - **`@Public()`** — an explicit, code-reviewed open route (login, health probes).
59
+ - **`@Classified({ typeKey, action })`** — a guarded route. The framework resolves the
60
+ request's bearer token → `Principal` → `decide()` and allows only on `allow`. No
61
+ token → 401; denied → 403.
62
+
63
+ An unclassified route is a 403, by design — a forgotten declaration locks the door
64
+ instead of silently exposing it. Never make a route public to "make it work"; classify
65
+ it correctly.
66
+
67
+ ## 4. When the framework can't do it: STOP and FLAG the gap — do NOT work around it
68
+
69
+ This is the most important rule. When a feature needs a capability the framework does
70
+ not provide — an unsupported field type, a missing engine operation, an auth capability
71
+ that isn't there — **you must STOP**. Do not:
72
+ - reach into or edit the core/framework to add it,
73
+ - hand-roll a raw-SQL / off-pipe workaround,
74
+ - weaken a classification or security check to get past it.
75
+
76
+ Instead, **record the gap** in `.astram/guide/gaps.md` (a short entry: what capability
77
+ is missing, where it was needed, why). The gap is a signal to the **framework team** to
78
+ implement the capability properly, into the framework. Once the framework provides it
79
+ (a new version), you resume the feature against the real capability.
80
+
81
+ The framework surfaces many gaps for you: the engine throws a typed
82
+ `CapabilityNotSupportedError` naming exactly what's missing. Treat that error as a STOP
83
+ signal, not something to catch-and-work-around.
84
+
85
+ ## 5. Never surface raw UUIDs (or secrets) to end users
86
+
87
+ Resolve internal ids to human-readable labels before they reach a UI or a response a
88
+ user sees. Never put a password, hash, token, or secret in a response body or a log.
89
+
90
+ ## 6. Auth is the framework's; you provide only DATA + POLICY
91
+
92
+ You do not write login/session/token code — the framework owns it (`ShieldModule
93
+ .forRoot({ auth: true })` gives you `POST /auth/{login,mfa,refresh,logout}` for free).
94
+ Your app contributes exactly two things through the seam:
95
+ - a **`CredentialResolver`** — reads YOUR user/identity tables to answer "what's this
96
+ user's password hash, tenant, roles?",
97
+ - a **`DecisionAuthority`** — your allow/deny policy.
98
+
99
+ Never reimplement session handling, token verification, or password hashing in app
100
+ code. If the auth surface lacks something you need, that's Rule 4 (flag the gap).
101
+
102
+ ## 7. Fail closed, always
103
+
104
+ On any ambiguity or error in a security path — deny. Never "allow on error", never
105
+ default-open, never swallow a security failure into a success.
106
+
107
+ ---
108
+
109
+ *These rules are the framework floor. Your project's rules (in `.astram/guide/`) add to
110
+ them. On upgrade, this file is refreshed (Rule 0) and your project rules are preserved.*
package/index.js ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @astram/guide — programmatic access to the framework guide docs.
3
+ *
4
+ * The docs (FRAMEWORK_RULES.md, CAPABILITIES.md) are the real payload; this module
5
+ * just exposes their absolute paths + the guide version so a consumer's sync step can
6
+ * copy them into `.claude/rules/` (see the scaffolded app's `sync-astram-guide` script).
7
+ * The version travels with the framework, so a consumer can detect an upgrade.
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ const { join } = require('node:path');
13
+
14
+ /** Absolute path to the framework hard-rules file (the floor, ADR-0025). */
15
+ const FRAMEWORK_RULES_PATH = join(__dirname, 'FRAMEWORK_RULES.md');
16
+
17
+ /** Absolute path to the capabilities manifest for this framework version. */
18
+ const CAPABILITIES_PATH = join(__dirname, 'CAPABILITIES.md');
19
+
20
+ /** The guide version (== the framework release it ships with). */
21
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
22
+ const { version } = require('./package.json');
23
+
24
+ module.exports = {
25
+ FRAMEWORK_RULES_PATH,
26
+ CAPABILITIES_PATH,
27
+ version,
28
+ };
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@astram/guide",
3
+ "version": "0.1.0",
4
+ "license": "MIT",
5
+ "description": "Astram framework — the AI/dev GUIDE layer (ADR-0025). Ships the framework's hard rules + capabilities manifest as installed, upgrade-refreshed docs. A consumer app's CLAUDE.md references FRAMEWORK_RULES.md from this package; `npm update` replaces it wholesale so new rules arrive cleanly, while the app's own .astram/guide/ layer is never touched. This is the framework-owned FLOOR: an app rule may tighten it, never weaken it.",
6
+ "type": "commonjs",
7
+ "main": "index.js",
8
+ "exports": {
9
+ ".": "./index.js",
10
+ "./FRAMEWORK_RULES.md": "./FRAMEWORK_RULES.md",
11
+ "./CAPABILITIES.md": "./CAPABILITIES.md",
12
+ "./package.json": "./package.json"
13
+ },
14
+ "files": [
15
+ "index.js",
16
+ "FRAMEWORK_RULES.md",
17
+ "CAPABILITIES.md"
18
+ ],
19
+ "sideEffects": false,
20
+ "publishConfig": {
21
+ "access": "public"
22
+ }
23
+ }