Open source preparation: licensing, credential externalization, and contributor docs - #22
Conversation
Move the PostHog project token, Sentry DSN, and Apple Team ID out of tracked source and into an xcconfig chain, so a build from a clean checkout carries no credentials and reports no telemetry anywhere. - Config.xcconfig is committed with blank defaults and optionally includes Config.local.xcconfig, which is gitignored. Contributors need no setup; a source build simply runs with telemetry disabled. - BuildConfiguration reads the values back from Info.plist and treats blank or unsubstituted values as "not configured". Sentry and PostHog initialization is now conditional on that. - xcconfig treats "//" as a comment, so URL-shaped values are stored without their scheme and reassembled at runtime. - DEVELOPMENT_TEAM is removed from all 20 sites in project.pbxproj so the xcconfig value takes effect; release.sh now injects the team into throwaway copies of the export options plists at release time. - Drop a hardcoded developer path in SampleDatabase that referenced an unreleased internal project, and a commented-out absolute package path. - Fill in .gitignore gaps: DerivedData, recursive xcuserdata, .swiftpm. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015BPk9Ma4asabJKM5Bq5WXW
Prepare the CI surface for a public repository. - Delete .github/workflows/sparkle.yml. It passed SPARKLE_SIGNING_KEY as argv[1], where it is visible in process listings and easily captured in logs; that key authorizes auto-updates for every installed copy of Minna. The workflow was also already broken, calling a deleted scripts/update_sparkle.sh. Sparkle itself is untouched: the vendored framework, the Info.plist feed and public key, the SPARKLE build flavor, and scripts/release.sh all remain, so releases stay a local operation. - Delete scripts/sentry.sh. It was dead code — release.sh makes its own sentry-cli debug-files upload calls and never invoked it — and it also took SENTRY_AUTH_TOKEN as a positional argument. - Gate claude.yml and claude-code-review.yml on github.actor. Both spend CLAUDE_CODE_OAUTH_TOKEN, and claude.yml previously fired on any issue comment containing "@claude", which on a public repository is an open invitation to spend the maintainer's token. - Document why claude-code-review.yml cannot run on fork pull requests, so the absence of runs is not later "fixed" by switching to pull_request_target and leaking secrets to untrusted code. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015BPk9Ma4asabJKM5Bq5WXW
Minna currently has no license file anywhere, which makes the source "all rights reserved" by default. Add the license and the attribution that redistributing third-party code requires. - Apache-2.0 LICENSE at the repository root and in each local package (DatabaseSchema, MinnaChat, ModelCDN, ModelUploader), verbatim from apache.org so the text stays byte-identical to the canonical file. - NOTICE names Impel Intelligence, Inc. as copyright holder and records the components redistributed here under other terms. - Sparkle 2.9.5 is vendored as 97 binary files with no license text; MIT redistribution requires the notice, so add Sparkle's LICENSE (which also carries the bsdiff and sais-lite external licenses) to the framework, and attribute the sample code adapted into Sparkle.swift. - CatchException.h/.m derive from a 2016 Stack Overflow answer, licensed CC BY-SA 3.0. Note that in the files themselves and in NOTICE so the provenance is not mistaken for Apache-2.0 code. - TRADEMARK.md states what Apache-2.0 section 6 already implies: the code is free to fork, the Minna name and marks are not. Under a permissive license this is the boundary that protects users from mistaking a fork for an official signed release. - Fill in NSHumanReadableCopyright, previously empty at all 8 sites. NOTICE also flags that several custom symbolsets were exported from Apple's SF Symbols tooling and are governed by Apple's license rather than Apache-2.0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015BPk9Ma4asabJKM5Bq5WXW
Minna has no CONTRIBUTING, CODE_OF_CONDUCT, SECURITY, or templates of any kind, which is workable for a single-author private repo and not workable for one accepting outside contributions. - CONTRIBUTING.md covers building (emphasizing that no configuration is required, since a blank Config.xcconfig is the supported default and people otherwise assume they missed a step), code style, the DCO sign-off, and the requirement that user-facing changes add a CHANGELOG entry. It states plainly that there is no CLA. - SECURITY.md gives a private reporting path and, more usefully, states exactly what Minna sends over the network — so a reviewer has something concrete to measure observed traffic against. - CODE_OF_CONDUCT.md is Contributor Covenant 2.1, verbatim. - Issue templates for bugs and features, with blank issues disabled and a contact link routing vulnerabilities to private advisories instead of public issues. The bug template warns before asking for logs, since Minna's logs can contain indexed file paths. - PULL_REQUEST_TEMPLATE.md, with a CHANGELOG checkbox and a check against committing credentials. - CODEOWNERS, weighted toward the files where an unreviewed change would be most costly: signing, licensing, workflows, release scripts. - docs/ARCHITECTURE.md orients a first-time reader around the app, IrisSearch, MinnaChat, and LookAtMe, and includes a "where to look" table. The only existing design docs live under docs/superpowers/, which is gitignored and would not ship. - README-OUTLINE.md is scaffolding for the README rewrite, noting that the current one still claims Anthropic is the only supported provider. It is meant to be deleted once the README is written. - CLAUDE.md now also requires a Co-Authored-By trailer on commits, extending the existing file and doc-comment attribution rules to history. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015BPk9Ma4asabJKM5Bq5WXW Signed-off-by: Taylor Lineman <git@actuallytaylor.com>
There was a problem hiding this comment.
Pull request overview
Prepares the repository for an open-source release by adding Apache-2.0 licensing/attribution, moving sensitive build-time credentials out of tracked source, tightening GitHub Actions triggers around secret-spending workflows, and adding contributor/security documentation.
Changes:
- Added Apache-2.0 licensing + NOTICE + third‑party attributions (including vendored Sparkle and StackOverflow-derived code).
- Externalized signing/telemetry configuration into an xcconfig chain and made Sentry/PostHog init conditional on configuration.
- Added contributor-facing docs/templates (CONTRIBUTING, SECURITY, CoC, issue/PR templates, CODEOWNERS, architecture doc) and removed a leaky/broken release workflow/script.
Reviewed changes
Copilot reviewed 38 out of 39 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| TRADEMARK.md | Adds trademark/brand usage policy alongside Apache-2.0 code licensing. |
| Tools/ModelUploader/LICENSE | Adds Apache-2.0 license for the ModelUploader tool. |
| SECURITY.md | Defines private vulnerability reporting and documents intended network behavior. |
| scripts/sentry.sh | Removes unused/dead script that accepted auth token positionally. |
| scripts/release.sh | Updates release pipeline to avoid committing team IDs; injects teamID into temp export plists. |
| scripts/export_options/SparkleExportOptions.plist | Blanks teamID for Sparkle export options to avoid tracking Apple Team ID. |
| scripts/export_options/AppStoreExportOptions.plist | Blanks teamID for App Store export options to avoid tracking Apple Team ID. |
| README-OUTLINE.md | Adds scaffolding for a future README rewrite with OSS-friendly structure. |
| Packages/ModelCDN/LICENSE | Adds Apache-2.0 license for ModelCDN package. |
| Packages/MinnaChat/LICENSE | Adds Apache-2.0 license for MinnaChat package. |
| Packages/DatabaseSchema/Package.swift | Removes a commented local absolute path reference. |
| Packages/DatabaseSchema/LICENSE | Adds Apache-2.0 license for DatabaseSchema package. |
| NOTICE | Adds project notice plus redistributed third-party attribution (Sparkle, CatchException, SF Symbols assets). |
| Minna/Utility/BuildConfiguration.swift | Introduces build-time config reader for optional telemetry/signing values. |
| Minna/Sparkle.swift | Adds attribution note for Sparkle sample-derived SwiftUI code. |
| Minna/objc/CatchException.m | Adds CC BY-SA attribution and clarifies licensing provenance. |
| Minna/objc/CatchException.h | Adds CC BY-SA attribution and clarifies licensing provenance. |
| Minna/IrisApp.swift | Makes Sentry/PostHog initialization conditional on build-time configuration. |
| Minna/Info.plist | Adds Info.plist keys for xcconfig substitution of telemetry values. |
| Minna/Database/Previews/SampleDatabase.swift | Replaces a hardcoded developer-local file path with a public placeholder URL. |
| Minna.xcodeproj/project.pbxproj | Adds xcconfig references, removes hardcoded team IDs, and fills NSHumanReadableCopyright. |
| LICENSE | Adds root Apache-2.0 license. |
| Frameworks/Sparkle.framework/LICENSE | Adds Sparkle MIT license text required for vendored redistribution. |
| docs/ARCHITECTURE.md | Adds an architecture overview for onboarding new contributors/reviewers. |
| CONTRIBUTING.md | Adds contribution, build, style, DCO, and AI-assistance guidance. |
| Config.xcconfig | Adds committed blank build config with optional include of gitignored local override. |
| Config.local.example.xcconfig | Adds a template for uncommitted local build configuration. |
| CODE_OF_CONDUCT.md | Adds Contributor Covenant 2.1 code of conduct. |
| CLAUDE.md | Updates AI contribution policy to require commit co-author trailers. |
| CHANGELOG.md | Adds unreleased entries for licensing and telemetry configuration changes. |
| .gitignore | Expands ignores for local config, build artifacts, and agent tooling. |
| .github/workflows/sparkle.yml | Deletes the Sparkle update workflow that handled signing keys unsafely (and was broken). |
| .github/workflows/claude.yml | Gates Claude workflow execution to a specific actor to protect secret spending. |
| .github/workflows/claude-code-review.yml | Gates Claude code-review workflow execution to a specific actor and documents fork behavior. |
| .github/PULL_REQUEST_TEMPLATE.md | Adds PR template with changelog/DCO/build/credential checklist. |
| .github/ISSUE_TEMPLATE/feature_request.yml | Adds feature request issue template. |
| .github/ISSUE_TEMPLATE/config.yml | Configures issue templates and contact links (incl. security). |
| .github/ISSUE_TEMPLATE/bug_report.yml | Adds bug report template with environment/provider prompts and security redirect. |
| .github/CODEOWNERS | Adds CODEOWNERS for sensitive areas (config, release, workflows, project file). |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Hard wrapping makes diffs noisy: editing one word reflows the paragraph and touches every line after it, so a one-word change shows up as a multi-line diff. Add the rule to CLAUDE.md and apply it to everything authored in this branch — Markdown, code and doc comments, xcconfig and gitignore comments, workflow comments, and issue and PR template text. Left wrapped deliberately, because the wrapping is part of text copied verbatim and reflowing it would corrupt the original: - LICENSE (canonical Apache-2.0 from apache.org, five copies) - Frameworks/Sparkle.framework/LICENSE (Sparkle 2.9.5, verbatim) - CODE_OF_CONDUCT.md (Contributor Covenant 2.1, verbatim) CLAUDE.md states that exception so the rule doesn't get applied to quoted material later. Verified after reformatting: all workflow and issue-template YAML parses, release.sh passes sh -n, and the xcconfig chain still resolves. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015BPk9Ma4asabJKM5Bq5WXW Signed-off-by: Taylor Lineman <git@actuallytaylor.com>
ActuallyTaylor
left a comment
There was a problem hiding this comment.
Looks good
Two changes, both aimed at the repository being worked on by more than one agent and reviewed automatically. Agent instructions now apply to any AI coding agent — Codex, Cursor, Copilot, Gemini — rather than Claude specifically. AGENTS.md is the canonical file, and CLAUDE.md is a symlink to it, so Claude Code still finds the filename it expects while there remains exactly one copy that cannot drift. The attribution rules ask every agent to identify itself specifically by model and vendor, in file headers, doc comments, and a Co-Authored-By commit trailer. Examples now show several agents rather than only Claude. Also clarified something the previous wording left ambiguous: DCO sign-off belongs to the human directing the work and is never something an agent signs for itself. The co-author trailer is separate and additional. Added .github/review-guidelines.md and pointed the automated reviewer at it. The workflow previously ran only the generic code-review plugin, which cannot know about this project's specific failure modes. The guidelines cover licensing and the currency of NOTICE and TRADEMARK.md when components or brand assets are added, secrets and build configuration, the privacy guarantees SECURITY.md makes to users, documentation drift, CHANGELOG entries, agent attribution, and release integrity. The criteria live in a checked-in file rather than in the workflow prompt so they are version controlled and reviewed like any other change, and so new feature areas can be appended without touching CI. The file closes with a note on how to extend it. Verified: workflow YAML parses and the prompt renders as intended; xcodebuild build of the Minna scheme succeeds with 0 errors. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015BPk9Ma4asabJKM5Bq5WXW Signed-off-by: Taylor Lineman <git@actuallytaylor.com>
Three fixes from automated review on #22. The signing team lookup aborted the script instead of falling back. With `set -e` and `set -o pipefail`, a grep that matches nothing exits 1 and fails the whole pipeline, so a Config.local.xcconfig without a DEVELOPMENT_TEAM line killed release.sh before the friendly "no signing team found" message could run. Added `|| true` and a comment explaining why it is load-bearing. Verified both ways: without it the script exits 1 silently, with it the error path is reached. Converted all 10 `$(PWD)` to `$PWD`. Worth noting these were not actually broken here, contrary to the review's reasoning that they expand to an empty string: on a case-insensitive filesystem `PWD` resolves to `/bin/pwd`, so the command substitution happened to work. The real hazard is a case-sensitive volume, where `PWD` does not resolve and `set -e` aborts the release. `$PWD` is correct regardless and avoids a subprocess. Corrected the gating comments on both Claude workflows. They claimed the jobs were "restricted to the repository owner" when the condition is a hardcoded login check. That distinction matters: it does not widen automatically when a collaborator is added, which is the exact assumption the old wording invited. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015BPk9Ma4asabJKM5Bq5WXW Signed-off-by: Taylor Lineman <git@actuallytaylor.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 40 out of 42 changed files in this pull request and generated no new comments.
Suppressed comments (7)
scripts/release.sh:141
- With
set -eandset -o pipefail, this pipeline will cause the script to exit early ifConfig.local.xcconfigexists but does not contain aDEVELOPMENT_TEAMassignment (becausegrepreturns 1). That makes the subsequent explicit "No signing team found" check unreachable in that case.
LICENSE:2 - This Apache-2.0 license text has a leading blank line, so it is not verbatim canonical as intended. Please remove the first empty line so the file starts directly with the "Apache License" heading.
Apache License
Packages/ModelCDN/LICENSE:2
- This package-level Apache-2.0 license text has a leading blank line, so it is not verbatim canonical as intended. Please remove the first empty line so the file starts directly with the "Apache License" heading.
Packages/MinnaChat/LICENSE:2 - This package-level Apache-2.0 license text has a leading blank line, so it is not verbatim canonical as intended. Please remove the first empty line so the file starts directly with the "Apache License" heading.
Packages/DatabaseSchema/LICENSE:2 - This package-level Apache-2.0 license text has a leading blank line, so it is not verbatim canonical as intended. Please remove the first empty line so the file starts directly with the "Apache License" heading.
Apache License
Tools/ModelUploader/LICENSE:2
- This tool-level Apache-2.0 license text has a leading blank line, so it is not verbatim canonical as intended. Please remove the first empty line so the file starts directly with the "Apache License" heading.
CODE_OF_CONDUCT.md:2 - This appears intended to be verbatim Contributor Covenant text, but it currently has a leading blank line. If the goal is to keep third-party legal text verbatim, remove the first empty line so the document starts at the heading.
# Contributor Covenant Code of Conduct
The section led with a rationale and a callback to a past README inaccuracy before getting to the actual criteria. The bullets state what to flag on their own. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015BPk9Ma4asabJKM5Bq5WXW Signed-off-by: Taylor Lineman <git@actuallytaylor.com>
ActuallyTaylor
left a comment
There was a problem hiding this comment.
Looks good
Stage A of preparing Minna for an open-source release. This changes no repository visibility and creates no release tags — it prepares the tree only.
Follows the assessment of what would block or embarrass a public release. The short version: the repo was in better shape than expected on the hard things — no private keys, certs,
.p8/.p12/.pem,.mobileprovision, or.envfiles anywhere in 291 commits — and blocked mostly on mechanical ones.Commits
56f8f81Externalize credentials and signing into Config.xcconfigThe PostHog token, Sentry DSN, and Apple Team ID were in tracked source. Now they come from an xcconfig chain:
Config.xcconfigis committed with every value blank and optionally includes a gitignoredConfig.local.xcconfigBuildConfiguration.swiftreads them back from Info.plist and treats blank or unsubstituted values as "not configured", so Sentry and PostHog initialization is conditionalDEVELOPMENT_TEAMremoved from all 20 sites inproject.pbxproj— inlinebuildSettingsoverride an xcconfig, so leaving them would have silently defeated the changerelease.shinjects the team into throwaway copies of the export plists at release timeA build from a clean checkout now reports no telemetry anywhere. Verified both directions with
xcodebuild -showBuildSettings: real values resolve with the local override present, nothing resolves without it.Note
This inverts what the plan specified (gitignore
Config.xcconfig, commitConfig.example.xcconfig). Committing the blank file with an#include?gives the same isolation, but contributors need zero setup and get no missing-file build warning.Also drops a hardcoded developer path in
SampleDatabase.swiftthat named an unreleased internal project, and fills.gitignoregaps.b4a9fb6Remove leaky release workflow and gate Claude workflows.github/workflows/sparkle.yml— it passedSPARKLE_SIGNING_KEYasargv[1], where it is visible in process listings and easily captured in logs. That key authorizes auto-updates for every installed copy of Minna. The workflow was already broken anyway, calling a deletedscripts/update_sparkle.sh. Sparkle itself is untouched — framework, Info.plist feed and public key,SPARKLEbuild flavor, andrelease.shall remain.scripts/sentry.sh— dead code (release.shmakes its ownsentry-clicalls and never invoked it), and it tookSENTRY_AUTH_TOKENpositionally.claude.ymlandclaude-code-review.ymlongithub.actor.claude.ymlpreviously fired on any issue comment containing@claude, which on a public repo is an open invitation to spendCLAUDE_CODE_OAUTH_TOKEN.claude-code-review.ymlcannot run on fork PRs, so its silence isn't later "fixed" by switching topull_request_targetand leaking secrets to untrusted code.b8c8669Apache-2.0 licensing, NOTICE, and third-party attributionThere was no license file anywhere, which makes the source "all rights reserved" by default.
NOTICEnames Impel Intelligence, Inc. and records what is redistributed under other termsCatchException.h/.mderive from a 2016 Stack Overflow answer — CC BY-SA 3.0, now attributed in the files and in NOTICE rather than passing as Apache-2.0 codeTRADEMARK.md— under a permissive license, the name is what lets users tell an official signed release from someone else's buildNSHumanReadableCopyrightfilled at all 8 previously-empty sites9bfcb2eContributor documentation and templatesCONTRIBUTING (DCO, no CLA, CHANGELOG requirement), SECURITY (private reporting plus an explicit list of every network destination, so reviewers have something concrete to measure traffic against), Contributor Covenant 2.1, issue/PR templates, CODEOWNERS,
docs/ARCHITECTURE.md, andREADME-OUTLINE.mdscaffolding.CLAUDE.mdnow also requires aCo-Authored-Bytrailer on commits.Found along the way, not in the plan
Several symbolsets (
brainchip.svg,brain-circuit.svg) were exported from Apple's SF Symbols tooling — the SVG headers say so. Apple's SF Symbols license permits use within apps but restricts redistribution and modification, which is a different posture from Apache-2.0. Documented inNOTICErather than shipped silently. Worth a look before publishing.Explicitly not done
SearchRouter.mlmodeland scrubbing the analytics values from history, plus stripping the IrisSearch and LookAtMe test corpora. Requires fresh clones (git-filter-repocannot run in a worktree), and must happen after this merges or it would just rewrite these commits.BlurbKit/ViewStorageLICENSE files — separate repos.IrisSearch/LookAtMesubmodules — separate repos; adding files there means commits needing their own push.README.mduntouched;README-OUTLINE.mdis scaffolding to write against and should be deleted afterward.Verification
xcodebuild -showBuildSettingsconfirms the xcconfig chain resolves correctly with and without the local overrideplutil -lintclean onproject.pbxprojand all plistssh -n scripts/release.shcleangit grepfor the PostHog token, Sentry DSN, Team ID, and/Users/taylorlinemanreturns nothing across tracked filesNot yet run: a full
xcodebuildcompile, andrelease.shend-to-end withsentry.shdeleted.🤖 Generated with Claude Code
https://claude.ai/code/session_015BPk9Ma4asabJKM5Bq5WXW