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 configuration from a YAML file, falling back to env vars. |
Classes
|
Local-runner AWS auth + QS embed-signing identity. |
|
|
|
Operator-side digital-signing material for audit PDF auto-sign (U.7.b). |
|
- class recon_gen.common.config.AuthConfig(aws_profile=None, quicksight_user_arn=None)[source]
Bases:
objectLocal-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
- 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
- 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. Hardcodingawsbreaks deploys against GovCloud / China where every account-bound resource ARN must carry the matching partition or QS rejects the binding.Resolution order:
If
datasource_arnis set explicitly (the customer supplied a pre-existing datasource), parse partition from it — that’s the authoritative shape for THIS account.Else if
principal_arnsis 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.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>. Thedeployment_nameis 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 cleanrequires both tags to match before deleting, so concurrent deploys (CI + local, dev + staging) never trample each other.
Returns
Nonewhentagging_enabled=Falseso the caller’sTags=cfg.tags()field assignment goes to the dataclass’sTags: list[Tag] | Nonefield asNoneand_strip_nonesdrops it from the emitted JSON entirely. Net effect: theCreate*boto3 call carries noTagskwarg, so the IAM principal doesn’t needquicksight:TagResourcepermission.- Return type:
list[Tag] |None
- test_generator: TestGeneratorConfig
- class recon_gen.common.config.SigningConfig(key_path, cert_path, passphrase_env=None, signer_name=None)[source]
Bases:
objectOperator-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/auditruns 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_envnames the env var holding the key passphrase if the key is encrypted (operator infrastructure stays out of the YAML).signer_nameis 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-hocdate.today()fallbacks through it, and AR’s views take an AsOfFrame as their anchor. Three resolution paths, one shape out:end_date == LOCKED_ANCHOR→AsOfFrame.locked()(the canonical demo anchor; locked-seed determinism).end_date is not None→ explicit-anchor frame (operator override or trainer-pinned).end_date is None→AsOfFrame.live()(production ends-at-now).
window_daysis an ergonomic shortcut: 0 means a single-day frame, N>0 means an N-day window ending at the anchor. BD.1 replaced the v1window_days: intFIELD on AsOfFrame with a typedwindow: DateIntervalfield; this kwarg stays at the construction seam (construction-time ergonomics ≠ runtime escape hatch).- Return type:
- 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_arnsmay be a single string or a list; a legacyprincipal_arnkey 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:
- Parameters:
path (str | Path | None)