recon_gen.common.config

Configuration for QuickSight resource generation.

Reads from a YAML config file or environment variables. All generated resources reference the datasource and account specified here.

Functions

load_config([path])

Load configuration from a YAML file, falling back to env vars.

Classes

AuthConfig([aws_profile, quicksight_user_arn])

Local-runner AWS auth + QS embed-signing identity.

Config(aws_account_id, aws_region, ...[, ...])

SigningConfig(key_path, cert_path[, ...])

Operator-side digital-signing material for audit PDF auto-sign (U.7.b).

TestGeneratorConfig([enabled, scope, ...])

class recon_gen.common.config.AuthConfig(aws_profile=None, quicksight_user_arn=None)[source]

Bases: object

Local-runner AWS auth + QS embed-signing identity.

Combined h+i.0 spike (2026-05-08, docs/audits/y_2_gate_h_i_combined_spike.md): long-lived IAM access keys for a dedicated recon-gen-local user, referenced from ~/.aws/credentials via a named profile. Eliminates the AWS-SSO-cache-miss browser flow that broke multi-hour Claude-loop sessions. Cfg yaml carries only the profile name; the keys themselves stay in ~/.aws/credentials (out of even gitignored cfg files, standard AWS pattern).

aws_profile — name of a profile in ~/.aws/credentials. Runner injects AWS_PROFILE=<value> into every subprocess it spawns. None = ambient AWS env (env vars / default profile / SSO cache).

quicksight_user_arn — explicit override for _derive_qs_user_arn’s auto-derivation. None = derive via sts:GetCallerIdentity + match on quicksight:ListUsers’s PrincipalId == “federated/iam/<UserId>”. Set explicitly when authed as a principal that doesn’t match the desired QS embed user (e.g., local-root authed but want test-user; CI’s per-job cfg with the secret value baked in).

Parameters:
  • aws_profile (str | None)

  • quicksight_user_arn (str | None)

aws_profile: str | None = None
quicksight_user_arn: str | None = None
class recon_gen.common.config.Config(aws_account_id, aws_region, deployment_name, db_table_prefix, datasource_arn=None, principal_arns=<factory>, extra_tags=<factory>, demo_database_url=None, dialect=Dialect.POSTGRES, signing=None, auth=None, default_l2_instance=None, tagging_enabled=True, studio_enabled=True, app2_db_pool_size=10, aws_pg_cluster_id=None, aws_oracle_instance_id=None, qs_disable_pg_ssl=False, etl_hook=None, test_generator=<factory>)[source]

Bases: object

Parameters:
  • aws_account_id (str)

  • aws_region (str)

  • deployment_name (str)

  • db_table_prefix (str)

  • datasource_arn (str | None)

  • principal_arns (list[str])

  • extra_tags (dict[str, str])

  • demo_database_url (str | None)

  • dialect (Dialect)

  • signing (SigningConfig | None)

  • auth (AuthConfig | None)

  • default_l2_instance (str | None)

  • tagging_enabled (bool)

  • studio_enabled (bool)

  • app2_db_pool_size (int)

  • aws_pg_cluster_id (str | None)

  • aws_oracle_instance_id (str | None)

  • qs_disable_pg_ssl (bool)

  • etl_hook (str | None)

  • test_generator (TestGeneratorConfig)

app2_db_pool_size: int = 10
auth: AuthConfig | None = None
aws_account_id: str
aws_oracle_instance_id: str | None = None
aws_pg_cluster_id: str | None = None
aws_region: str
dataset_arn(dataset_id)[source]
Return type:

str

Parameters:

dataset_id (str)

datasource_arn: str | None = None
datasource_arn_was_derived: bool = False
db_table_prefix: str
default_l2_instance: str | None = None
demo_database_url: str | None = None
deployment_name: str
dialect: Dialect = 'postgres'
etl_hook: str | None = None
extra_tags: dict[str, str]
property partition: str

AWS partition for synthesized ARNs.

Standard commercial AWS = aws; GovCloud = aws-us-gov; China = aws-cn. Hardcoding aws breaks deploys against GovCloud / China where every account-bound resource ARN must carry the matching partition or QS rejects the binding.

Resolution order:

  1. If datasource_arn is set explicitly (the customer supplied a pre-existing datasource), parse partition from it — that’s the authoritative shape for THIS account.

  2. Else if principal_arns is non-empty, parse from the first principal ARN — the customer’s user/role is in the same partition as the resources we’re about to synthesize.

  3. Else default aws (commercial; preserves prior behavior for the spec_example / fuzz fixtures that don’t carry a principal).

Bare strings (no arn: prefix) fall through to the default.

prefixed(name)[source]

Return a resource ID with the configured deployment prefix.

Z.C: single-segment prefix replaces v8.x’s <resource_prefix>-<l2_instance_prefix>-<name>. The deployment_name is the operator’s per-deployment namespace, set explicitly in cfg.yaml (no default).

Return type:

str

Parameters:

name (str)

principal_arns: list[str]
qs_disable_pg_ssl: bool = False
signing: SigningConfig | None = None
studio_enabled: bool = True
tagging_enabled: bool = True
tags()[source]

Return common + extra tags as the AWS Tag list format.

Two tags are always emitted (when tagging_enabled):

  • ManagedBy=recon-gen — gates cleanup eligibility (the tool-identity signal; never varies).

  • Deployment=<deployment_name> — per-deploy scope. json clean requires both tags to match before deleting, so concurrent deploys (CI + local, dev + staging) never trample each other.

Returns None when tagging_enabled=False so the caller’s Tags=cfg.tags() field assignment goes to the dataclass’s Tags: list[Tag] | None field as None and _strip_nones drops it from the emitted JSON entirely. Net effect: the Create* boto3 call carries no Tags kwarg, so the IAM principal doesn’t need quicksight:TagResource permission.

Return type:

list[Tag] | None

test_generator: TestGeneratorConfig
theme_arn(theme_id)[source]
Return type:

str

Parameters:

theme_id (str)

class recon_gen.common.config.SigningConfig(key_path, cert_path, passphrase_env=None, signer_name=None)[source]

Bases: object

Operator-side digital-signing material for audit PDF auto-sign (U.7.b).

When the audit apply –execute writes a PDF and the loaded config carries a signing: block, cli/audit runs it through pyHanko to apply a CMS signature over the entire PDF bytes. The system-attestation block on the sign-off page becomes the cryptographically-bound artifact.

The signature is incremental so subsequent signers (auditor, second reviewer, regulator) can add their own signatures on top via Adobe / pyHanko / any compliant tool — the document is deliberately silent on how many signatures are required.

PEM RSA key + PEM cert; passphrase_env names the env var holding the key passphrase if the key is encrypted (operator infrastructure stays out of the YAML). signer_name is the free-form display name shown in the signature widget; defaults to the cert’s CN when None.

Parameters:
  • key_path (str)

  • cert_path (str)

  • passphrase_env (str | None)

  • signer_name (str | None)

cert_path: str
key_path: str
passphrase_env: str | None = None
signer_name: str | None = None
class recon_gen.common.config.TestGeneratorConfig(enabled=True, scope='full', end_date=None, seed=None, plants=(), only_template=None, derive_balances=False, derive_balances_account_roles=None, cutoff_date=None)[source]

Bases: object

Parameters:
  • enabled (bool)

  • scope (Literal['full', 'exceptions_only', 'uncovered_rails', 'only_template'])

  • end_date (date | None)

  • seed (int | None)

  • plants (tuple[Literal['drift', 'overdraft', 'limit_breach', 'stuck_pending', 'stuck_unbundled', 'supersession'], ...])

  • only_template (str | None)

  • derive_balances (bool)

  • derive_balances_account_roles (tuple[str, ...] | None)

  • cutoff_date (date | None)

as_of_frame(*, window_days=0)[source]

Resolve this config’s scenario anchor as the owned AsOfFrame (D1; see docs/audits/date_range_model_audit.md §5 + BD.0 spike).

This is the call-site every as_of reader lands on — AQ.3 funnels the generator’s threaded anchor= and the four ad-hoc date.today() fallbacks through it, and AR’s views take an AsOfFrame as their anchor. Three resolution paths, one shape out:

  • end_date == LOCKED_ANCHORAsOfFrame.locked() (the canonical demo anchor; locked-seed determinism).

  • end_date is not None → explicit-anchor frame (operator override or trainer-pinned).

  • end_date is NoneAsOfFrame.live() (production ends-at-now).

window_days is an ergonomic shortcut: 0 means a single-day frame, N>0 means an N-day window ending at the anchor. BD.1 replaced the v1 window_days: int FIELD on AsOfFrame with a typed window: DateInterval field; this kwarg stays at the construction seam (construction-time ergonomics ≠ runtime escape hatch).

Return type:

AsOfFrame

Parameters:

window_days (int)

cutoff_date: date | None = None
derive_balances: bool = False
derive_balances_account_roles: tuple[str, ...] | None = None
enabled: bool = True
end_date: date | None = None
only_template: str | None = None
plants: tuple[Literal['drift', 'overdraft', 'limit_breach', 'stuck_pending', 'stuck_unbundled', 'supersession'], ...] = ()
scope: Literal['full', 'exceptions_only', 'uncovered_rails', 'only_template'] = 'full'
seed: int | None = None
recon_gen.common.config.load_config(path=None)[source]

Load configuration from a YAML file, falling back to env vars.

YAML keys map directly to Config fields (snake_case). principal_arns may be a single string or a list; a legacy principal_arn key is also accepted as a single string. Environment variables use uppercase with RECON_GEN_ prefix:

RECON_GEN_AWS_ACCOUNT_ID, RECON_GEN_AWS_REGION, RECON_GEN_DATASOURCE_ARN, RECON_GEN_RESOURCE_PREFIX, RECON_GEN_PRINCIPAL_ARNS (comma-separated)

V.1.b: rejects unknown YAML keys and L2-only keys (theme, persona, rails, etc.) with a pointer to the L2 institution YAML.

Return type:

Config

Parameters:

path (str | Path | None)