Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,27 @@ The supported `v2.0.0` bootstrap flow is app-first:
apw app install
apw app launch
apw doctor --json
apw login https://example.com
apw login https://vault.example.com
```

The current bootstrap domain is `https://example.com`. The APW app uses a
same-user local broker socket and explicit approval UI for the returned
credential flow.
In a notarized build with associated-domain entitlements wired,
`apw login` routes through the
[`AuthenticationServicesBroker`](native-app/Sources/NativeAppLib/AuthenticationServicesBroker.swift)
and returns an iCloud Keychain credential surfaced via the Apple
credential picker (issue #13).

A separate **demo bootstrap path** is available for first-run
validation. Setting `APW_DEMO=1` makes the broker materialize and
return the bundled placeholder credential for `https://example.com` —
nothing else. Without `APW_DEMO=1`, the demo path returns a typed
`no_credential_source` error rather than silently falling back to a
plaintext file (issue #14):

```bash
APW_DEMO=1 apw app install
APW_DEMO=1 apw app launch
APW_DEMO=1 apw login https://example.com
```

Optional reduced-security mode for external password managers can be configured
in `~/.apw/config.json` with an absolute provider path:
Expand Down
105 changes: 105 additions & 0 deletions docs/DOMAIN_EXPANSION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Adding production domains to the APW v2 native app

Issue: [#8](https://github.com/OMT-Global/apw-cli/issues/8)

The `v2.0.0` native app supports `https://example.com` as the bundled
demo associated domain. Operators who want APW to broker credentials
for additional domains must extend the macOS `Associated Domains`
entitlement, host an `apple-app-site-association` (AASA) file at each
target domain, and re-sign / re-notarize the rebuilt bundle.

This document is the operator playbook for that work.

## Prerequisites

- macOS with Xcode and a valid `Developer ID Application` certificate
(run `apw doctor` to confirm — issue #12).
- Apple Notary credentials wired into release CI (issue #7).
- Write access to the DNS / `/.well-known` path of every target domain.

## Step 1: list the domains in `~/.apw/config.json`

Add (or update) the `supportedDomains` array in the user config. The
field is validated against the bundle's `Associated Domains` entitlement
at runtime, so it cannot claim more domains than the app is entitled to.

```json
{
"schema": 1,
"supportedDomains": [
"example.com",
"vault.acme.example",
"internal.acme.example"
]
}
```

## Step 2: extend the app entitlement

Edit `native-app/Sources/NativeApp/APW.entitlements` and add one
`webcredentials:<domain>` entry per target domain inside the
`com.apple.developer.associated-domains` array. Example:

```xml
<key>com.apple.developer.associated-domains</key>
<array>
<string>webcredentials:example.com</string>
<string>webcredentials:vault.acme.example</string>
<string>webcredentials:internal.acme.example</string>
</array>
```

Wildcards (`webcredentials:*.acme.example`) are allowed but each base
domain must still serve a valid AASA file.

## Step 3: serve a valid AASA file at each domain

Each target domain must serve a publicly-reachable AASA file at:

```
https://<domain>/.well-known/apple-app-site-association
```

The file must be served as `application/json`, must not redirect, and
must include the `webcredentials.apps` array with the APW bundle id:

```json
{
"webcredentials": {
"apps": ["<TEAM_ID>.dev.omt.apw"]
}
}
```

`<TEAM_ID>` is the 10-character Apple Developer Team ID that signs the
APW.app bundle.

Apple's CDN caches AASA files aggressively; allow up to 24h between an
AASA update and end-user broker behavior.

## Step 4: rebuild, re-sign, re-notarize

```bash
./scripts/build-native-app.sh
# Sign with the Developer ID Application certificate (release.yml will
# automate this once issue #7 lands).
xcrun notarytool submit native-app/dist/APW.app.zip --wait \
--key "$APPLE_NOTARY_PRIVATE_KEY" \
--key-id "$APPLE_NOTARY_KEY_ID" \
--issuer "$APPLE_NOTARY_KEY_ISSUER"
xcrun stapler staple native-app/dist/APW.app
apw app install
```

## Step 5: verify with `apw doctor`

Run `apw doctor --json` after install. The `app.frameworks` block
reports the entitlement domains the bundle was signed with, and the
`environment` array (issue #12) probes reachability of each AASA file
under `app.aasa[]`. Any check that fails surfaces a remediation hint.

## Long-term plan

A multi-tenant entitlement (wildcard subdomain or managed capability)
would remove the per-domain rebuild requirement. That investigation is
captured under issue #8 and is not yet scheduled.
17 changes: 17 additions & 0 deletions docs/MIGRATION_AND_PARITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,23 @@ Release reference version: `v2.0.0`
- Legacy daemon commands (`apw start`, `apw auth`, `apw pw`, and `apw otp`)
emit runtime deprecation warnings and are targeted for removal in `v2.1.0`.

## Planned removals

The following CLI subcommands are part of the legacy daemon path and are
scheduled for removal in **v2.1.0**. As of `v2.0.0` they emit a one-line
stderr deprecation warning at startup (suppressed in `--json` mode) and
their `--help` output is prefixed with a `DEPRECATED:` banner. (issue #9)

| Subcommand | Replacement |
| ------------ | ---------------------------- |
| `apw start` | `apw app launch` |
| `apw pw` | `apw login` / `apw fill` |
| `apw otp` | (no v2 replacement planned) |
| `apw auth` | (no v2 replacement; v2 broker is app-mediated) |

Operators with scripts pinned to these commands should migrate before
upgrading to v2.1.0.

Archive rules: [ARCHIVE_POLICY.md](ARCHIVE_POLICY.md)

## Parity target
Expand Down
25 changes: 25 additions & 0 deletions docs/NATIVE_ONLY_REDESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,31 @@ Deliverables:
- end-to-end sign-in flow for one associated domain
- stable error mapping for cancel, denial, timeout, and unsupported-domain cases

#### Phase 3 status (issue #13)

- `native-app/Sources/NativeAppLib/AuthenticationServicesBroker.swift`
introduces a `CredentialBroker` protocol, the
`AppleAuthenticationServicesBroker` implementation that drives
`ASAuthorizationController` + `ASAuthorizationPasswordRequest` on the
main thread and bridges results back to the worker thread via
`DispatchSemaphore`, and a stable `BrokerErrorCode` mapping
(`canceled` / `failed` / `invalidResponse` / `notHandled` / `unknown`).
- `BrokerCore.loginResponse` routes through the injected broker when
`APW_DEMO` is unset, mapping outcomes onto the existing wire envelope
(`transport: "authentication_services"`, `userMediated: true`, integer
status codes that match the Rust `Status` enum).
- `BrokerCoreTests` exercises the broker outcome paths via
`StubCredentialBroker` for `success` / `denied` / `canceled` /
`invalidResponse`, and asserts the broker error code mapping.

**Phase 3 exit blockers still open**:

- The integration is unverified against a notarized build with
associated-domain entitlements wired (the macOS build cannot be
exercised from CI on Linux). A follow-up validation pass on a real
macOS host is required before declaring Phase 3 complete.
- Domain expansion beyond `example.com` is tracked in issue #8.

### Phase 4: command migration and deprecation

- Add compatibility warnings to `pw` and `otp`
Expand Down
31 changes: 31 additions & 0 deletions docs/bootstrap/onboarding.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Bootstrap Onboarding

## Local environment check

- Run `apw doctor` from a fresh checkout — the first-step diagnostic for new
contributors. It probes `xcodebuild`, `rustc`, `detect-secrets`, the
Apple `Developer ID Application` keychain identity, and the APW.app
bundle install state, and prints a `[OK]/[WARN]/[FAIL]` line per check
with a remediation hint.
- For CI consumers and runner inventory work, `apw doctor --ci` emits the
same checks as a structured JSON array (also honors the global `--json`
flag). When `CI=true`, set `RUNNER_LABELS` so the doctor can sanity-check
the runner pool selection (issue #12).

## Repo Governance

This manifest update prepares the desired GitHub governance state, but it does
Expand Down Expand Up @@ -33,6 +45,25 @@
- Run `scripts/bump-version.sh <version>` from the repository root to update all version-bearing release surfaces.
- Run `bash scripts/ci/run-fast-checks.sh` after version bumps before opening a release PR.

### Release secrets

The following repository secrets are consumed by `.github/workflows/release.yml`:

| Secret | Purpose |
| ---------------------------- | ------------------------------------------------------------- |
| `APPLE_DEVELOPER_CERT_P12` | base64-encoded Developer ID Application .p12 (issue #7) |
| `APPLE_CERT_PASSWORD` | passphrase for the .p12 above |
| `APPLE_TEAM_ID` | 10-character Apple Developer Team ID |
| `APPLE_NOTARY_KEY_ID` | App Store Connect API key id used by `notarytool` |
| `APPLE_NOTARY_KEY_ISSUER` | App Store Connect issuer UUID |
| `APPLE_NOTARY_PRIVATE_KEY` | base64-encoded `.p8` private key for `notarytool` |
| `HOMEBREW_TAP_TOKEN` | scoped `contents:write` token on the tap repo (issue #6) |

All Apple credentials are optional — when absent, the workflow emits
a `::warning::` and continues without notarization. The Homebrew tap
job is `continue-on-error` so a missing or rejected token does not
block the release.

## Home Profiles

- Run `project-bootstrap apply home --manifest ./project.bootstrap.yaml` after reviewing the bundled profile content.
Expand Down
Loading
Loading