@atlasauth/pca-ciba 0.0.0-stage → 0.2.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Atlas Authorization
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,64 @@
1
- # Temporary Holding Version
1
+ # @atlasauth/pca-ciba
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A CIBA (OpenID Connect Client-Initiated Backchannel Authentication) on-ramp for Proof-Carrying Authority (PCA) step-up. It turns a risk-gated step-up into a standard asynchronous human-approval flow, interrupting a person only for the actions that actually need it.
4
+
5
+ In CIBA a backend opens an `auth_req_id`, the human is notified out of band, and the decision comes back by poll or ping/push. The `binding_message` carries the human-readable approval context, so the thing approved on the phone is the thing the agent is about to do ("Approve stripe.refund $480 on charge:ch_1 - because you asked support-bot to 'reconcile October refunds'").
6
+
7
+ This is a bridge, not a replacement. PCA's cryptographic step-up (a FROST threshold cosign, in `@atlasauth/pca`) stays the enforcement; CIBA is the notification and decision envelope around it.
8
+
9
+ Gating is risk-adaptive: only a tier-3 step-up (human required, typically irreversible) is routed through CIBA. Tier-2 (guardian auto-cosign) and tier-1 (`auto`) never interrupt a person.
10
+
11
+ ## Install
12
+
13
+ ```sh
14
+ npm i @atlasauth/pca-ciba @atlasauth/pca
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ```ts
20
+ import { agent, generateKeyPair, reviewAction } from '@atlasauth/pca';
21
+ import { requiresCiba, createCibaBroker, cibaDecisionToCosign } from '@atlasauth/pca-ciba';
22
+
23
+ const a = agent({
24
+ principal: generateKeyPair(),
25
+ goal: 'reconcile October refunds',
26
+ permissions: { stripe: ['payout'] },
27
+ limits: { payout: '$100' },
28
+ aud: 'rs_demo',
29
+ riskPolicy: { theta1: 0.3, theta2: 0.6 },
30
+ });
31
+
32
+ const review = reviewAction(a, 'stripe.payout', 'acct:1', { amount: 80 }, { goal: 'reconcile October refunds' });
33
+
34
+ if (review.kind === 'step_up' && requiresCiba(review)) {
35
+ const broker = createCibaBroker({ onComplete: (session) => console.log(session.status) }); // omit onComplete for POLL
36
+ const ciba = broker.start(review.request, { actsFor: 'support-bot', loginHint: 'owner@acme' });
37
+ // ciba.auth_req_id, ciba.binding_message, ciba.requested_expiry, ...
38
+
39
+ broker.poll(ciba.auth_req_id).status; // 'pending' | 'approved' | 'denied' | 'expired'
40
+
41
+ // The human decides out of band:
42
+ const session = broker.resolve(ciba.auth_req_id, 'approve', 'owner@acme');
43
+
44
+ // Fold the decision back into the trust budget:
45
+ const { budget, cosigned } = cibaDecisionToCosign(
46
+ session.status === 'approved' ? 'approve' : 'deny',
47
+ a.budget,
48
+ a.policy.riskPolicy,
49
+ Date.now(),
50
+ );
51
+ }
52
+ ```
53
+
54
+ `toCibaAuthRequest(stepUp, opts?)` is the pure mapping from a step-up to a CIBA request, if you want to drive your own broker or a real OpenID provider.
55
+
56
+ ## Status
57
+
58
+ The broker is in-memory and models the CIBA lifecycle without any network; a production deployment would swap in a real OpenID provider. The package computes approval envelopes and the derived trust budget and authorises nothing on its own: `cibaDecisionToCosign` is only the budget-level effect. The cryptographic cosign (FROST) is produced and verified separately, and the resource server's verifier remains the authority. Cryptography in PCA is unaudited.
59
+
60
+ Source and issues: https://github.com/Atlas-Authorization/pca
61
+
62
+ ## License
63
+
64
+ MIT - see LICENSE
@@ -0,0 +1,147 @@
1
+ /**
2
+ * @atlasauth/pca-ciba — a CIBA on-ramp for PCA's FROST step-up.
3
+ *
4
+ * CIBA (OpenID Connect Client-Initiated Backchannel Authentication, OpenID CIBA Core) is the
5
+ * convergent human-in-the-loop (HITL) primitive every modern IdP ships — Auth0 ("CIBA / push
6
+ * authorization"), Descope, Stytch — for *asynchronous* out-of-band approval: a backend starts an
7
+ * `auth_req_id`, the human is notified on a separate device, and the backend learns the decision by
8
+ * polling (POLL) or via a callback (PING/PUSH). Its `binding_message` field carries the short
9
+ * human-readable context the person sees while approving, so the thing they approve on their phone is
10
+ * provably the thing the agent is about to do.
11
+ *
12
+ * This package is a BRIDGE, not a replacement. PCA's cryptographic step-up (FROST threshold cosign —
13
+ * see `@atlasauth/pca` `threshold`/`frost`) stays the *enforcement*: a tier-3 action proceeds only once
14
+ * a real threshold signature recharges the trust budget. CIBA is only the human-notification / decision
15
+ * *envelope* around that cosign — it standardises how the human is asked and how the answer comes back.
16
+ * PCA's FROST step-up is cryptographically stronger than a bare CIBA approval (an OP's yes/no is a
17
+ * trusted single party; a FROST cosign is an unforgeable t-of-n signature); this just gives it the
18
+ * industry-standard async-approval + binding-message on-ramp.
19
+ *
20
+ * Risk-adaptive gating is the point. If every action prompts a human, humans blanket-approve (Anthropic
21
+ * observed 93% blanket-approval under always-prompt). So only a tier-3 step-up — a human-required,
22
+ * typically irreversible/high-risk action — is routed through CIBA. Tier-2 (guardian auto-cosign) and
23
+ * tier-1 (`auto`) never interrupt a person. `requiresCiba()` encodes exactly that gate.
24
+ *
25
+ * Everything here is pure or in-memory and authorises nothing on its own: the resource server's verifier
26
+ * (`requirePCA` / the adjudicator) and the FROST cosigners remain the run-time authority.
27
+ */
28
+ import { type StepUpRequest, type Review, type TrustBudget, type RiskPolicy } from '@atlasauth/pca';
29
+ /** A CIBA backchannel authentication request derived from a PCA step-up. */
30
+ export interface CibaAuthRequest {
31
+ /**
32
+ * The CIBA `auth_req_id` — the opaque handle the backend polls / correlates on. Derived
33
+ * deterministically from the step-up id, so the same step-up always maps to the same request.
34
+ */
35
+ auth_req_id: string;
36
+ /**
37
+ * The human-readable approval context shown to the person (OpenID CIBA `binding_message`): what is
38
+ * being approved and why, traced to the goal the agent was given.
39
+ */
40
+ binding_message: string;
41
+ /** CIBA `scope` (space-delimited), carrying `openid` + a PCA step-up scope for this verb. */
42
+ scope: string;
43
+ /** CIBA `requested_expiry` in SECONDS — how long the human has to decide. */
44
+ requested_expiry: number;
45
+ /** CIBA `interval` in SECONDS — minimum poll spacing for POLL mode. */
46
+ interval: number;
47
+ /** CIBA `login_hint` — who to notify, if the caller supplied one. */
48
+ login_hint?: string;
49
+ /** The PCA step-up this request fulfils (back-link for correlation). */
50
+ stepUpId: string;
51
+ /** The PCA risk tier that produced the step-up (2 guardian / 3 human). */
52
+ tier: 2 | 3;
53
+ }
54
+ /** Options shared by `toCibaAuthRequest` and the broker's `start`. */
55
+ export interface CibaRequestOptions {
56
+ /** Override the `requested_expiry` (seconds). Default 300. */
57
+ expiresInSec?: number;
58
+ /** CIBA `login_hint` for the OP: the identifier of the human to notify. */
59
+ loginHint?: string;
60
+ /**
61
+ * The agent's human-facing name used in the binding message ("because you asked <actsFor> to …"),
62
+ * e.g. `"support-bot"`. Default `"your agent"`.
63
+ */
64
+ actsFor?: string;
65
+ }
66
+ /**
67
+ * Map a PCA {@link StepUpRequest} to a {@link CibaAuthRequest}. Pure: derives a stable `auth_req_id`
68
+ * from the step-up id, renders the `binding_message`, and carries the scope / expiry / poll interval.
69
+ * Does not start a flow or notify anyone — that's the broker.
70
+ */
71
+ export declare function toCibaAuthRequest(stepUp: StepUpRequest, opts?: CibaRequestOptions): CibaAuthRequest;
72
+ /** The settled outcome of a CIBA request, with `pending` while the human hasn't acted and `expired`
73
+ * once `requested_expiry` elapses. */
74
+ export type CibaStatus = 'pending' | 'approved' | 'denied' | 'expired';
75
+ /** A broker session: the request plus the (lazily-evaluated) decision state. */
76
+ export interface CibaSession {
77
+ auth_req_id: string;
78
+ request: CibaAuthRequest;
79
+ /** Effective status computed at read time (never a stale stored flag). */
80
+ status: CibaStatus;
81
+ /** ms epoch the session was started. */
82
+ createdAt: number;
83
+ /** ms epoch the request expires (`createdAt + requested_expiry·1000`). */
84
+ expiresAt: number;
85
+ /** Who acted, once resolved. */
86
+ decidedBy?: string;
87
+ /** ms epoch the human acted, once resolved. */
88
+ decidedAt?: number;
89
+ }
90
+ /** Options for {@link createCibaBroker}. */
91
+ export interface CibaBrokerOptions {
92
+ /** Clock (ms). Default `Date.now`. Inject for deterministic tests / expiry. */
93
+ now?: () => number;
94
+ /**
95
+ * PING/PUSH mode: called once, synchronously, the moment a session is resolved (approved or denied).
96
+ * Omit it to use pure POLL mode (the caller drives `poll`).
97
+ */
98
+ onComplete?: (session: CibaSession) => void;
99
+ }
100
+ /** The in-memory CIBA broker returned by {@link createCibaBroker}. */
101
+ export interface CibaBroker {
102
+ /** Begin a backchannel flow for a step-up; returns the CIBA auth request (status starts `pending`). */
103
+ start(stepUp: StepUpRequest, opts?: CibaRequestOptions): CibaAuthRequest;
104
+ /** POLL: the current effective status (`authorization_pending` semantics while `pending`). */
105
+ poll(auth_req_id: string): {
106
+ status: CibaStatus;
107
+ };
108
+ /** The human acts out-of-band: approve or deny. Fires `onComplete` (PING/PUSH) if configured. */
109
+ resolve(auth_req_id: string, decision: 'approve' | 'deny', by: string): CibaSession;
110
+ /** Read a full session snapshot (status computed at read time), or `undefined` if unknown. */
111
+ get(auth_req_id: string): CibaSession | undefined;
112
+ }
113
+ /**
114
+ * An in-memory CIBA broker. Models the backchannel lifecycle without any network: `start` opens a
115
+ * session, `poll` reports `pending`/`approved`/`denied`/`expired`, and `resolve` records the human's
116
+ * decision. Supports both delivery modes — POLL (caller drives `poll`) and PING/PUSH (an `onComplete`
117
+ * callback fires on resolution). A production deployment swaps this for the real OP + FROST cosigners;
118
+ * the shape stays the same.
119
+ */
120
+ export declare function createCibaBroker(opts?: CibaBrokerOptions): CibaBroker;
121
+ /**
122
+ * The gate: does this review need a CIBA human-approval round-trip? Only a tier-3 step-up (human
123
+ * required — typically irreversible / high blast-radius) does. A tier-2 step-up is handled by the
124
+ * guardian's auto-cosign, and an `auto`/`deny` review never notifies a human. This is the
125
+ * risk-adaptive interrupt that avoids blanket-approval fatigue.
126
+ */
127
+ export declare function requiresCiba(review: Review): boolean;
128
+ /** The budget effect of a settled CIBA decision. */
129
+ export interface CibaCosignResult {
130
+ /** The trust budget after applying the decision (recharged on approve, unchanged on deny). */
131
+ budget: TrustBudget;
132
+ /** Whether a (budget-level) cosign was applied. */
133
+ cosigned: boolean;
134
+ }
135
+ /**
136
+ * Fold a settled CIBA decision back into PCA's trust budget. On `approve`, recharge via `applyCosign`
137
+ * (partial ρ per cosign, or full with `{ full: true }`); on `deny`, the budget is unchanged.
138
+ *
139
+ * NOTE: this is the BUDGET-level effect only. The actual cryptographic cosign that authorises a tier-3
140
+ * action is a FROST threshold signature (`@atlasauth/pca` `threshold`/`frost`), produced out of band and
141
+ * verified by the resource server — CIBA carries the human *decision*, not the signature. A real
142
+ * integration gates `applyCosign` on a verified FROST cosign; here we model the budget recharge the
143
+ * approval authorises.
144
+ */
145
+ export declare function cibaDecisionToCosign(decision: 'approve' | 'deny', budget: TrustBudget, policy: RiskPolicy, now: number, opts?: {
146
+ full?: boolean;
147
+ }): CibaCosignResult;
package/dist/index.js ADDED
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ /**
3
+ * @atlasauth/pca-ciba — a CIBA on-ramp for PCA's FROST step-up.
4
+ *
5
+ * CIBA (OpenID Connect Client-Initiated Backchannel Authentication, OpenID CIBA Core) is the
6
+ * convergent human-in-the-loop (HITL) primitive every modern IdP ships — Auth0 ("CIBA / push
7
+ * authorization"), Descope, Stytch — for *asynchronous* out-of-band approval: a backend starts an
8
+ * `auth_req_id`, the human is notified on a separate device, and the backend learns the decision by
9
+ * polling (POLL) or via a callback (PING/PUSH). Its `binding_message` field carries the short
10
+ * human-readable context the person sees while approving, so the thing they approve on their phone is
11
+ * provably the thing the agent is about to do.
12
+ *
13
+ * This package is a BRIDGE, not a replacement. PCA's cryptographic step-up (FROST threshold cosign —
14
+ * see `@atlasauth/pca` `threshold`/`frost`) stays the *enforcement*: a tier-3 action proceeds only once
15
+ * a real threshold signature recharges the trust budget. CIBA is only the human-notification / decision
16
+ * *envelope* around that cosign — it standardises how the human is asked and how the answer comes back.
17
+ * PCA's FROST step-up is cryptographically stronger than a bare CIBA approval (an OP's yes/no is a
18
+ * trusted single party; a FROST cosign is an unforgeable t-of-n signature); this just gives it the
19
+ * industry-standard async-approval + binding-message on-ramp.
20
+ *
21
+ * Risk-adaptive gating is the point. If every action prompts a human, humans blanket-approve (Anthropic
22
+ * observed 93% blanket-approval under always-prompt). So only a tier-3 step-up — a human-required,
23
+ * typically irreversible/high-risk action — is routed through CIBA. Tier-2 (guardian auto-cosign) and
24
+ * tier-1 (`auto`) never interrupt a person. `requiresCiba()` encodes exactly that gate.
25
+ *
26
+ * Everything here is pure or in-memory and authorises nothing on its own: the resource server's verifier
27
+ * (`requirePCA` / the adjudicator) and the FROST cosigners remain the run-time authority.
28
+ */
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ exports.toCibaAuthRequest = toCibaAuthRequest;
31
+ exports.createCibaBroker = createCibaBroker;
32
+ exports.requiresCiba = requiresCiba;
33
+ exports.cibaDecisionToCosign = cibaDecisionToCosign;
34
+ const pca_1 = require("@atlasauth/pca");
35
+ const DEFAULT_EXPIRY_SEC = 300;
36
+ const DEFAULT_INTERVAL_SEC = 5;
37
+ /** A short "$480" / "480 EUR" fragment from a step-up's params, or "" when there's no amount. */
38
+ function amountFragment(params) {
39
+ if (!params)
40
+ return '';
41
+ const amt = params['amount'];
42
+ if (typeof amt !== 'number' || !Number.isFinite(amt))
43
+ return '';
44
+ const cur = params['currency'];
45
+ if (typeof cur === 'string' && cur.length > 0 && cur.toLowerCase() !== 'usd') {
46
+ return ` ${amt} ${cur.toUpperCase()}`;
47
+ }
48
+ return ` $${amt}`;
49
+ }
50
+ /**
51
+ * Build the CIBA `binding_message`: the one-line human-readable approval context. Combines the
52
+ * verb + optional amount + resource with the goal-lineage ("because you asked <agent> to '<goal>'").
53
+ * Falls back to the salted goal commitment when the plaintext goal isn't carried on the step-up, so
54
+ * lineage is always present.
55
+ */
56
+ function bindingMessage(stepUp, actsFor) {
57
+ const head = `Approve ${stepUp.verb}${amountFragment(stepUp.params)} on ${stepUp.resource}`;
58
+ const goalText = stepUp.goal !== undefined && stepUp.goal.length > 0
59
+ ? stepUp.goal
60
+ : stepUp.goalCommit.length > 0
61
+ ? `goal ${stepUp.goalCommit.slice(0, 10)}…`
62
+ : undefined;
63
+ return goalText === undefined ? head : `${head} — because you asked ${actsFor} to '${goalText}'`;
64
+ }
65
+ /**
66
+ * Map a PCA {@link StepUpRequest} to a {@link CibaAuthRequest}. Pure: derives a stable `auth_req_id`
67
+ * from the step-up id, renders the `binding_message`, and carries the scope / expiry / poll interval.
68
+ * Does not start a flow or notify anyone — that's the broker.
69
+ */
70
+ function toCibaAuthRequest(stepUp, opts = {}) {
71
+ const actsFor = opts.actsFor !== undefined && opts.actsFor.length > 0 ? opts.actsFor : 'your agent';
72
+ const auth_req_id = (0, pca_1.hashCanonical)({ d: 'atlas-pca/ciba/auth_req_id/v1', stepUp: stepUp.id });
73
+ return {
74
+ auth_req_id,
75
+ binding_message: bindingMessage(stepUp, actsFor),
76
+ scope: `openid urn:atlas:pca:stepup:${stepUp.verb}`,
77
+ requested_expiry: opts.expiresInSec !== undefined ? opts.expiresInSec : DEFAULT_EXPIRY_SEC,
78
+ interval: DEFAULT_INTERVAL_SEC,
79
+ ...(opts.loginHint !== undefined ? { login_hint: opts.loginHint } : {}),
80
+ stepUpId: stepUp.id,
81
+ tier: stepUp.tier,
82
+ };
83
+ }
84
+ /** Status is computed, never stored — a decision wins, else expiry, else pending (read-time truth). */
85
+ function effectiveStatus(s, now) {
86
+ if (s.decision === 'approve')
87
+ return 'approved';
88
+ if (s.decision === 'deny')
89
+ return 'denied';
90
+ if (now >= s.expiresAt)
91
+ return 'expired';
92
+ return 'pending';
93
+ }
94
+ function snapshot(s, now) {
95
+ return {
96
+ auth_req_id: s.request.auth_req_id,
97
+ request: s.request,
98
+ status: effectiveStatus(s, now),
99
+ createdAt: s.createdAt,
100
+ expiresAt: s.expiresAt,
101
+ ...(s.decidedBy !== undefined ? { decidedBy: s.decidedBy } : {}),
102
+ ...(s.decidedAt !== undefined ? { decidedAt: s.decidedAt } : {}),
103
+ };
104
+ }
105
+ /**
106
+ * An in-memory CIBA broker. Models the backchannel lifecycle without any network: `start` opens a
107
+ * session, `poll` reports `pending`/`approved`/`denied`/`expired`, and `resolve` records the human's
108
+ * decision. Supports both delivery modes — POLL (caller drives `poll`) and PING/PUSH (an `onComplete`
109
+ * callback fires on resolution). A production deployment swaps this for the real OP + FROST cosigners;
110
+ * the shape stays the same.
111
+ */
112
+ function createCibaBroker(opts = {}) {
113
+ const clock = opts.now ?? Date.now;
114
+ const sessions = new Map();
115
+ return {
116
+ start(stepUp, reqOpts) {
117
+ const request = toCibaAuthRequest(stepUp, reqOpts);
118
+ const createdAt = clock();
119
+ const stored = {
120
+ request,
121
+ createdAt,
122
+ expiresAt: createdAt + request.requested_expiry * 1000,
123
+ };
124
+ sessions.set(request.auth_req_id, stored);
125
+ return request;
126
+ },
127
+ poll(auth_req_id) {
128
+ const s = sessions.get(auth_req_id);
129
+ if (!s)
130
+ throw new Error(`ciba: unknown auth_req_id ${auth_req_id}`);
131
+ return { status: effectiveStatus(s, clock()) };
132
+ },
133
+ resolve(auth_req_id, decision, by) {
134
+ const s = sessions.get(auth_req_id);
135
+ if (!s)
136
+ throw new Error(`ciba: unknown auth_req_id ${auth_req_id}`);
137
+ const now = clock();
138
+ const current = effectiveStatus(s, now);
139
+ if (current !== 'pending') {
140
+ throw new Error(`ciba: cannot resolve ${auth_req_id} — already ${current}`);
141
+ }
142
+ s.decision = decision;
143
+ s.decidedBy = by;
144
+ s.decidedAt = now;
145
+ const snap = snapshot(s, now);
146
+ if (opts.onComplete)
147
+ opts.onComplete(snap);
148
+ return snap;
149
+ },
150
+ get(auth_req_id) {
151
+ const s = sessions.get(auth_req_id);
152
+ return s ? snapshot(s, clock()) : undefined;
153
+ },
154
+ };
155
+ }
156
+ // ---- risk-adaptive gating + the hand-off back to the cryptographic cosign -------------------------
157
+ /**
158
+ * The gate: does this review need a CIBA human-approval round-trip? Only a tier-3 step-up (human
159
+ * required — typically irreversible / high blast-radius) does. A tier-2 step-up is handled by the
160
+ * guardian's auto-cosign, and an `auto`/`deny` review never notifies a human. This is the
161
+ * risk-adaptive interrupt that avoids blanket-approval fatigue.
162
+ */
163
+ function requiresCiba(review) {
164
+ return review.kind === 'step_up' && review.request.tier === 3;
165
+ }
166
+ /**
167
+ * Fold a settled CIBA decision back into PCA's trust budget. On `approve`, recharge via `applyCosign`
168
+ * (partial ρ per cosign, or full with `{ full: true }`); on `deny`, the budget is unchanged.
169
+ *
170
+ * NOTE: this is the BUDGET-level effect only. The actual cryptographic cosign that authorises a tier-3
171
+ * action is a FROST threshold signature (`@atlasauth/pca` `threshold`/`frost`), produced out of band and
172
+ * verified by the resource server — CIBA carries the human *decision*, not the signature. A real
173
+ * integration gates `applyCosign` on a verified FROST cosign; here we model the budget recharge the
174
+ * approval authorises.
175
+ */
176
+ function cibaDecisionToCosign(decision, budget, policy, now, opts = {}) {
177
+ if (decision === 'approve')
178
+ return { budget: (0, pca_1.applyCosign)(budget, policy, now, opts), cosigned: true };
179
+ return { budget, cosigned: false };
180
+ }
181
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,57 @@
1
1
  {
2
2
  "name": "@atlasauth/pca-ciba",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.2.0",
4
+ "description": "CIBA (Client-Initiated Backchannel Authentication) bridge for PCA FROST step-up: turn a step-up request into a backchannel human-approval flow with risk-adaptive gating.",
5
+ "keywords": [
6
+ "ciba",
7
+ "backchannel",
8
+ "human approval",
9
+ "step-up",
10
+ "agent authorization",
11
+ "proof-carrying authority",
12
+ "pca",
13
+ "ai agents"
14
+ ],
15
+ "license": "MIT",
16
+ "homepage": "https://github.com/Atlas-Authorization/pca#readme",
17
+ "bugs": {
18
+ "url": "https://github.com/Atlas-Authorization/pca/issues"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/Atlas-Authorization/pca.git",
23
+ "directory": "packages/pca-ciba"
24
+ },
25
+ "main": "./dist/index.js",
26
+ "types": "./dist/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "default": "./dist/index.js"
31
+ },
32
+ "./package.json": "./package.json"
33
+ },
34
+ "dependencies": {
35
+ "@atlasauth/pca": "0.2.0"
36
+ },
37
+ "devDependencies": {
38
+ "typescript": "^5.6.3",
39
+ "vitest": "^2.1.4"
40
+ },
41
+ "files": [
42
+ "dist",
43
+ "README.md",
44
+ "LICENSE"
45
+ ],
46
+ "engines": {
47
+ "node": ">=20"
48
+ },
49
+ "publishConfig": {
50
+ "access": "public"
51
+ },
52
+ "scripts": {
53
+ "build": "tsc -p tsconfig.build.json",
54
+ "typecheck": "tsc -p tsconfig.json --noEmit",
55
+ "test": "vitest run"
56
+ }
6
57
  }