Project · Contributing

Contributing

Patches, tests, doc fixes, distro updates, bug reports — all welcome. Read this first so your contribution lands on the first review.

First-time setup

  1. Fork & clone:
    gh repo fork DaxxSec/SecVF --clone
    # or
    git clone https://github.com/<you>/SecVF.git
    cd SecVF
    git remote add upstream https://github.com/DaxxSec/SecVF.git
  2. Build: open SecVF.xcodeproj, pick the SecVF scheme, hit R.
  3. Run tests: U in Xcode, or xcodebuild test -scheme SecVF -destination 'platform=macOS'.
  4. Build the CLI: cd SecVF/cli && swift build.
  5. Optional but nice: install SwiftFormat (brew install swiftformat) and the project's pre-commit hook (./scripts/install-hooks.sh if present).

PR workflow

  1. Open an issue first for non-trivial changes — saves you from building something we won't merge.
  2. Branch from main: git checkout -b feature/your-change. Conventional branch prefixes: feature/, fix/, docs/, test/, refactor/.
  3. Keep commits atomic. One logical change per commit. Squash before PR if you have noise.
  4. Conventional commits: feat:, fix:, docs:, test:, refactor:, chore:. First line ≤72 chars. Body explains why, not what.
  5. Rebase, don't merge. Keep history linear: git pull --rebase upstream main.
  6. Open the PR against main. Fill the template. Link the issue.
  7. Address review. Push fixups; we'll squash on merge.

Coding conventions

Swift style

  • 4-space indent. SwiftFormat applied.
  • Match existing patterns before introducing new ones. Three similar lines is better than a premature abstraction.
  • UI classes are @MainActor. Domain singletons are @MainActor. Internal mutating state hides behind serial queues.
  • Errors are typed (SecVFError), not NSError. Add a new case with a LocalizedError recovery suggestion.
  • Prefer protocols at boundaries (any VMManagerProtocol) over concrete types — keeps tests easy.

Comments

Comment why, never what. A well-named function doesn't need a doc comment that paraphrases its body. Comment when:

  • The code is a workaround for a specific bug — link the issue or PR.
  • There's a hidden invariant a future reader would miss.
  • You did something non-obvious for performance — explain the alternative you rejected.

File organization

One main type per file. Filename matches the type. Group MARK:-delimited sections inside large files. Don't refactor file structure as part of a feature PR — that's a separate PR.

Tests

Tests use XCTest. Convention: Given-When-Then. Each test name reads as a sentence.

func test_givenStoppedVM_whenStarted_thenStateBecomesRunning() async throws {
    // Given
    let manager = MockVMManager()
    let vm = TestHelpers.makeMockVM(name: "test", state: .stopped)
    manager.addVM(vm)

    // When
    try await manager.startVM(name: "test")

    // Then
    XCTAssertEqual(manager.vm(named: "test")?.state, .running)
}

Use TestHelpers.swift for mock construction. Add helpers there rather than duplicating fixtures.

Required test coverage per PR:

  • New domain method → at least one happy-path test, one failure-path test.
  • Bug fix → a test that fails without the fix and passes with it. Commit the test first if you can.
  • UI change → if a logic path is added; pure visual changes don't need tests.
  • CLI command → one functional test invoking through the protocol mock.

PR checklist

Copy this into your PR description and tick off:

- [ ] Linked the issue this addresses (or explained why no issue).
- [ ] Tests added/updated; all green.
- [ ] No new public API without a docstring.
- [ ] Followed `@MainActor` rules for UI and domain singletons.
- [ ] Logs the right severity for new events (INFO / WARNING / CRITICAL).
- [ ] CHANGELOG.md updated (under "Unreleased").
- [ ] No GPL-licensed code introduced (see §License).
- [ ] No new third-party dependencies without discussion in the issue.
- [ ] If you touched network/security code: tagged the maintainer.
- [ ] Self-reviewed the diff — no debug prints, no commented-out blocks.

License & CLA

The repo is MIT-licensed. By submitting a PR, you agree your contribution is offered under the same license. There's no separate CLA to sign.

The GPL trap — please read

SecVF dual-distributes (free direct download + Mac App Store). The App Store path is incompatible with GPL/LGPL/AGPL — Apple's EULA imposes restrictions that conflict with GPL §6.

Do not:

  • Submit code copied or adapted from GPL'd projects.
  • Add Swift Package Manager dependencies on GPL'd libraries.
  • Port a GPL-licensed plugin or dissector.

If you have something you adapted from a GPL'd source — even at the design level — flag it in the PR. We'll figure out whether the design alone is okay (often it is) or whether we need to rewrite from a different reference.

For the full reasoning see docs/LICENSE-REVIEW.md.

Trademark

MIT covers code, not branding. The name SecVF and the shield logo are not granted by the source license.

  • Forking is fine. You can fork, distribute, and modify the source.
  • Renaming when distributing is mandatory. If you publish a fork, rename it. Don't call your fork "SecVF" or use the logo without permission.
  • App Store branding as "SecVF" is reserved for the canonical maintainer (this is the lever that lets the App Store version stay the recognizable product even though the source is open).

This is standard practice — see UTM, VLC, WireGuard for how other projects handle the same line.

Easy first contributions

Want to dip a toe in? Pick one of these — small, useful, learns the codebase.

  • Add a Linux distro to Resources/distros.json. The schema is documented inline. Test by creating a VM with your new distro.
  • Write a test for an uncovered method. Run xcodebuild -showCoverage; pick something at 0% coverage.
  • Wiki improvements — typos, clarifications, new troubleshooting entries. The whole wiki is in website/wiki/ as plain HTML.
  • CLI ergonomics — short-flag aliases, better error messages on common mistakes.
  • A new helper command on the router VM — extend scripts/kali-router-setup.sh. Add a doc entry on Router VM § helpers.

Browse issues tagged good first issue for curated starter tasks.

Don't hesitate to ask questions on the PR or in the issue. We'd rather you check before spending a weekend on the wrong approach. Bad questions don't exist; bad surprises do.