feat: Update bundle identifiers and add logging configuration profile

- Updated macOS test bundle IDs to use consistent naming pattern:
  - sh.vibetunnel.vibetunnelTests → sh.vibetunnel.vibetunnel.tests
  - sh.vibetunnel.vibetunnelTests.debug → sh.vibetunnel.vibetunnel.tests.debug
- Updated iOS test bundle ID:
  - sh.vibetunnel.VibeTunnelTests-Mobile → sh.vibetunnel.ios.tests
- Fixed iOS logging to use sh.vibetunnel.ios subsystem consistently
- Created logging configuration profile in apple/logging/ to enable full debug logging
- Configuration profile covers all VibeTunnel subsystems and bundle IDs
- Updated documentation to reflect new bundle identifiers and logging setup
This commit is contained in:
Peter Steinberger 2025-07-30 18:04:32 +02:00
parent 97d51fbe8d
commit eab1e6c962
14 changed files with 393 additions and 27 deletions

View file

@ -948,6 +948,60 @@ sudo tccutil reset ScreenCapture sh.vibetunnel.vibetunnel.debug # For debug bui
sudo tccutil reset AppleEvents
```
## Logging and Privacy
VibeTunnel uses Apple's unified logging system with the subsystem `sh.vibetunnel.vibetunnel`. By default, macOS redacts sensitive runtime data in logs, showing `<private>` instead of actual values. This is a privacy feature to prevent accidental exposure of sensitive information.
### Bundle Identifiers
VibeTunnel uses the following bundle identifiers:
**Production:**
- `sh.vibetunnel.vibetunnel` - Main macOS app and logging subsystem
- `sh.vibetunnel.vibetunnel.debug` - Debug builds of the macOS app
**Testing:**
- `sh.vibetunnel.vibetunnel.tests` - macOS test suite
- `sh.vibetunnel.ios.tests` - iOS test suite
**iOS:**
- `sh.vibetunnel.ios` - iOS keychain service and URL scheme
### Viewing Unredacted Logs
To see full log details for debugging, you have several options:
1. **Use the vtlog script with sudo** (reveals private data):
```bash
sudo ./scripts/vtlog.sh --info
```
2. **Configure passwordless sudo** for the log command:
```bash
# Add to sudoers (replace 'yourusername' with your actual username)
sudo visudo
# Add this line:
yourusername ALL=(ALL) NOPASSWD: /usr/bin/log
```
3. **Enable private data logging** using a plist file (recommended):
```bash
# Create the plist to enable private data for VibeTunnel
sudo mkdir -p /Library/Preferences/Logging/Subsystems
sudo tee /Library/Preferences/Logging/Subsystems/sh.vibetunnel.vibetunnel.plist > /dev/null << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Enable-Private-Data</key>
<true/>
</dict>
</plist>
EOF
```
For more detailed information about logging privacy and additional methods, see [apple/docs/logging-private-fix.md](apple/docs/logging-private-fix.md).
## Contributing
We welcome contributions! VibeTunnel is a community-driven project and we'd love to have you join us.

View file

@ -1,5 +1,35 @@
# Fixing macOS Log Redaction for VibeTunnel
## Quick Fix: Configuration Profile (Recommended)
We provide a ready-to-use configuration profile that enables full debug logging for VibeTunnel:
**Location**: `apple/logging/VibeTunnel-Logging.mobileconfig`
### Installing the Profile
#### macOS
1. Double-click `apple/logging/VibeTunnel-Logging.mobileconfig`
2. System Settings will open to the Profiles section
3. Click "Install..." and enter your password
4. Restart VibeTunnel
#### iOS
1. AirDrop or email the profile to your device
2. Open Settings → General → VPN & Device Management
3. Install the "VibeTunnel Debug Logging" profile
4. Restart the VibeTunnel app
### Verifying It Works
```bash
# You should now see full details instead of <private>
./scripts/vtlog.sh
```
### Removing the Profile
- **macOS**: System Settings → Privacy & Security → Profiles → Remove
- **iOS**: Settings → General → VPN & Device Management → Remove Profile
## The Problem
When viewing VibeTunnel logs using Apple's unified logging system, you'll see `<private>` instead of actual values:
@ -133,18 +163,83 @@ logger.info("Connected to \(sessionId)")
logger.info("Connected to \(sessionId, privacy: .public)")
```
### 4. Configure logging system
### 4. Configure logging system (Modern Methods)
#### Method A: Plist File (Recommended)
Create a plist file to enable private data logging for VibeTunnel:
Temporarily enable private data for all VibeTunnel logs:
```bash
sudo log config --mode "private_data:on" --subsystem sh.vibetunnel.vibetunnel
# Create the directory if it doesn't exist
sudo mkdir -p /Library/Preferences/Logging/Subsystems
# Create the plist file
sudo tee /Library/Preferences/Logging/Subsystems/sh.vibetunnel.vibetunnel.plist > /dev/null << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Enable-Private-Data</key>
<true/>
</dict>
</plist>
EOF
# Verify it was created
ls -la /Library/Preferences/Logging/Subsystems/sh.vibetunnel.vibetunnel.plist
```
To revert:
To remove:
```bash
sudo log config --mode "private_data:off" --subsystem sh.vibetunnel.vibetunnel
sudo rm /Library/Preferences/Logging/Subsystems/sh.vibetunnel.vibetunnel.plist
```
#### Method B: Configuration Profile
For managed environments or multiple subsystems, create a configuration profile:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PayloadContent</key>
<array>
<dict>
<key>PayloadType</key>
<string>com.apple.system.logging</string>
<key>PayloadIdentifier</key>
<string>com.example.logging.vibetunnel</string>
<key>PayloadUUID</key>
<string>$(uuidgen)</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>Subsystems</key>
<dict>
<key>sh.vibetunnel.vibetunnel</key>
<dict>
<key>Enable-Private-Data</key>
<true/>
</dict>
</dict>
</dict>
</array>
<key>PayloadIdentifier</key>
<string>com.example.vibetunnel.logging</string>
<key>PayloadUUID</key>
<string>$(uuidgen)</string>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
</plist>
```
Install via System Settings → Profiles.
**Note:** The old `private_data:on` flag was removed in macOS Catalina and no longer works.
## Using vtlog.sh
With passwordless sudo configured, you can now use:

79
apple/logging/README.md Normal file
View file

@ -0,0 +1,79 @@
# VibeTunnel Logging Configuration Profile
This directory contains the configuration profile for enabling full debug logging in VibeTunnel apps.
## What It Does
The `VibeTunnel-Logging.mobileconfig` profile enables:
- Debug-level logging for both macOS and iOS apps
- Visibility of private data (no more `<private>` tags)
- Persistent logging at debug level
## Installation
### macOS
1. Double-click `VibeTunnel-Logging.mobileconfig`
2. System Settings will open
3. Go to Privacy & Security → Profiles
4. Click on "VibeTunnel Debug Logging"
5. Click "Install..."
6. Enter your password when prompted
7. Restart VibeTunnel for changes to take effect
### iOS
1. AirDrop or email the `VibeTunnel-Logging.mobileconfig` to your iOS device
2. Tap the file to open it
3. iOS will prompt to review the profile
4. Go to Settings → General → VPN & Device Management
5. Tap on "VibeTunnel Debug Logging"
6. Tap "Install" and enter your passcode
7. Restart the VibeTunnel app
## Verification
After installation, logs should show full details:
```bash
# macOS - using vtlog script
./scripts/vtlog.sh
# iOS - in Xcode console or Console.app
# You should see actual values instead of <private>
```
## Removal
### macOS
1. System Settings → Privacy & Security → Profiles
2. Select "VibeTunnel Debug Logging"
3. Click the minus (-) button
4. Confirm removal
### iOS
1. Settings → General → VPN & Device Management
2. Tap "VibeTunnel Debug Logging"
3. Tap "Remove Profile"
4. Enter passcode to confirm
## Security Note
This profile enables detailed logging which may include sensitive information. Only install on development devices and remove when no longer needed for debugging.
## Technical Details
The profile configures logging for all VibeTunnel subsystems:
### macOS
- `sh.vibetunnel.vibetunnel` - Main macOS app and all components
- `sh.vibetunnel.vibetunnel.debug` - Debug builds
- `sh.vibetunnel.vibetunnel.tests` - Test suite
- `sh.vibetunnel.vibetunnel.tests.debug` - Debug test builds
### iOS
- `sh.vibetunnel.vibetunnel-mobile` - Main iOS app bundle ID
- `sh.vibetunnel.ios` - Alternative iOS bundle ID (used in services)
- `sh.vibetunnel.ios.tests` - iOS test suite
All subsystems are configured to:
- Enable at Debug level
- Persist at Debug level
- Show private data (no `<private>` redaction)

View file

@ -0,0 +1,138 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PayloadContent</key>
<array>
<dict>
<key>PayloadType</key>
<string>com.apple.system.logging</string>
<key>PayloadIdentifier</key>
<string>sh.vibetunnel.logging.all</string>
<key>PayloadUUID</key>
<string>A7B8C9D0-1234-5678-9ABC-DEF012345678</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadDisplayName</key>
<string>VibeTunnel Logging Configuration</string>
<key>PayloadDescription</key>
<string>Enables private data in logs for all VibeTunnel apps and components</string>
<key>Subsystems</key>
<dict>
<!-- Main macOS app and all its components -->
<key>sh.vibetunnel.vibetunnel</key>
<dict>
<key>DEFAULT-OPTIONS</key>
<dict>
<key>Level</key>
<dict>
<key>Enable</key>
<string>Debug</string>
<key>Persist</key>
<string>Debug</string>
</dict>
</dict>
</dict>
<!-- macOS debug build -->
<key>sh.vibetunnel.vibetunnel.debug</key>
<dict>
<key>DEFAULT-OPTIONS</key>
<dict>
<key>Level</key>
<dict>
<key>Enable</key>
<string>Debug</string>
<key>Persist</key>
<string>Debug</string>
</dict>
</dict>
</dict>
<!-- macOS tests -->
<key>sh.vibetunnel.vibetunnel.tests</key>
<dict>
<key>DEFAULT-OPTIONS</key>
<dict>
<key>Level</key>
<dict>
<key>Enable</key>
<string>Debug</string>
<key>Persist</key>
<string>Debug</string>
</dict>
</dict>
</dict>
<!-- macOS tests debug -->
<key>sh.vibetunnel.vibetunnel.tests.debug</key>
<dict>
<key>DEFAULT-OPTIONS</key>
<dict>
<key>Level</key>
<dict>
<key>Enable</key>
<string>Debug</string>
<key>Persist</key>
<string>Debug</string>
</dict>
</dict>
</dict>
<!-- iOS app (main bundle ID) -->
<key>sh.vibetunnel.vibetunnel-mobile</key>
<dict>
<key>DEFAULT-OPTIONS</key>
<dict>
<key>Level</key>
<dict>
<key>Enable</key>
<string>Debug</string>
<key>Persist</key>
<string>Debug</string>
</dict>
</dict>
</dict>
<!-- iOS app (alternative bundle ID) -->
<key>sh.vibetunnel.ios</key>
<dict>
<key>DEFAULT-OPTIONS</key>
<dict>
<key>Level</key>
<dict>
<key>Enable</key>
<string>Debug</string>
<key>Persist</key>
<string>Debug</string>
</dict>
</dict>
</dict>
<!-- iOS tests -->
<key>sh.vibetunnel.ios.tests</key>
<dict>
<key>DEFAULT-OPTIONS</key>
<dict>
<key>Level</key>
<dict>
<key>Enable</key>
<string>Debug</string>
<key>Persist</key>
<string>Debug</string>
</dict>
</dict>
</dict>
</dict>
</dict>
</array>
<key>PayloadIdentifier</key>
<string>sh.vibetunnel.logging</string>
<key>PayloadUUID</key>
<string>C9D0E1F2-3456-7890-1CDE-F23456789012</string>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadDisplayName</key>
<string>VibeTunnel Debug Logging</string>
<key>PayloadDescription</key>
<string>Enables full debug logging with private data visibility for VibeTunnel applications. Install this profile to see complete log details instead of &lt;private&gt; tags.</string>
<key>PayloadOrganization</key>
<string>VibeTunnel</string>
</dict>
</plist>

View file

@ -164,7 +164,7 @@ vtlog | grep ServerConfig
vtlog -v
# Monitor specific subsystem
vtlog --subsystem sh.vibetunnel.vibetunnel
vtlog --subsystem sh.vibetunnel.ios
```
### Code Quality

View file

@ -333,7 +333,7 @@
ENABLE_TESTING_FRAMEWORKS = YES;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
PRODUCT_BUNDLE_IDENTIFIER = "sh.vibetunnel.VibeTunnelTests-Mobile";
PRODUCT_BUNDLE_IDENTIFIER = "sh.vibetunnel.ios.tests";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_VERSION = 6.0;
@ -426,7 +426,7 @@
ENABLE_TESTING_FRAMEWORKS = YES;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
PRODUCT_BUNDLE_IDENTIFIER = "sh.vibetunnel.VibeTunnelTests-Mobile";
PRODUCT_BUNDLE_IDENTIFIER = "sh.vibetunnel.ios.tests";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_VERSION = 6.0;

View file

@ -78,7 +78,7 @@
<string>vibetunnel</string>
</array>
<key>CFBundleURLName</key>
<string>com.vibetunnel.app</string>
<string>sh.vibetunnel.ios</string>
</dict>
</array>
<key>CFBundleDocumentTypes</key>

View file

@ -4,7 +4,7 @@ import Security
/// Service for securely storing credentials in the iOS Keychain.
/// Provides secure storage and retrieval of passwords and tokens.
class KeychainService: KeychainServiceProtocol {
private let serviceName = "com.vibetunnel.ios"
private let serviceName = "sh.vibetunnel.ios"
/// Errors that can occur during keychain operations.
/// Provides specific error cases for keychain failures.

View file

@ -42,8 +42,8 @@ struct Logger {
init(category: String) {
self.category = category
// Use the same subsystem as the Mac app for consistency
self.osLogger = os.Logger(subsystem: "sh.vibetunnel.vibetunnel", category: category)
// Use iOS-specific subsystem for proper log filtering
self.osLogger = os.Logger(subsystem: "sh.vibetunnel.ios", category: category)
}
func verbose(_ message: String) {

View file

@ -300,14 +300,14 @@ struct AuthenticationTests {
}
let item = KeychainItem(
service: "com.vibetunnel.app",
service: "sh.vibetunnel.ios",
account: "user-token",
data: "secret-token".data(using: .utf8)!,
accessGroup: nil
)
#expect(item.query[kSecClass as String] as? String == kSecClassGenericPassword as String)
#expect(item.query[kSecAttrService as String] as? String == "com.vibetunnel.app")
#expect(item.query[kSecAttrService as String] as? String == "sh.vibetunnel.ios")
#expect(item.query[kSecAttrAccount as String] as? String == "user-token")
}

View file

@ -257,7 +257,7 @@ struct FileSystemTests {
@Test("Sandbox path validation")
func sandboxPaths() {
struct SandboxValidator {
let appGroupIdentifier = "group.com.vibetunnel"
let appGroupIdentifier = "group.sh.vibetunnel"
var documentsDirectory: String {
"~/Documents"

View file

@ -6,7 +6,7 @@
set -euo pipefail
# Configuration
SUBSYSTEM="sh.vibetunnel.vibetunnel"
SUBSYSTEM="sh.vibetunnel.ios"
DEFAULT_LEVEL="info"
# Colors for output

View file

@ -512,7 +512,7 @@
GENERATE_INFOPLIST_FILE = YES;
MACOSX_DEPLOYMENT_TARGET = 14.0;
MARKETING_VERSION = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = sh.vibetunnel.vibetunnelTests.debug;
PRODUCT_BUNDLE_IDENTIFIER = sh.vibetunnel.vibetunnel.tests.debug;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VibeTunnel.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/VibeTunnel";
@ -529,7 +529,7 @@
GENERATE_INFOPLIST_FILE = YES;
MACOSX_DEPLOYMENT_TARGET = 14.0;
MARKETING_VERSION = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = sh.vibetunnel.vibetunnelTests;
PRODUCT_BUNDLE_IDENTIFIER = sh.vibetunnel.vibetunnel.tests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VibeTunnel.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/VibeTunnel";

View file

@ -2107,8 +2107,8 @@ body.initial-session-load .session-flex-responsive > session-card:nth-child(n +
/* Custom styling */
width: 1rem;
height: 1rem;
background-color: rgb(var(--color-bg-primary));
border: 2px solid rgb(var(--color-border) / 0.5);
background-color: var(--color-bg);
border: 2px solid color-mix(in srgb, var(--color-border) 50%, transparent);
border-radius: 3px;
position: relative;
cursor: pointer;
@ -2122,14 +2122,14 @@ body.initial-session-load .session-flex-responsive > session-card:nth-child(n +
/* Hover state */
.session-toggle-checkbox:hover:not(:checked) {
border-color: rgb(var(--color-border));
background-color: rgb(var(--color-bg-secondary));
border-color: var(--color-border);
background-color: var(--color-bg-secondary);
}
/* Checked state */
.session-toggle-checkbox:checked {
background-color: rgb(var(--color-primary) / 0.15);
border-color: rgb(var(--color-primary) / 0.5);
background-color: color-mix(in srgb, var(--color-primary) 15%, transparent);
border-color: color-mix(in srgb, var(--color-primary) 50%, transparent);
}
/* Checkmark using pseudo-element */
@ -2139,7 +2139,7 @@ body.initial-session-load .session-flex-responsive > session-card:nth-child(n +
height: 0.5rem;
transform: scale(0);
transition: transform 0.15s ease-in-out;
background-color: rgb(var(--color-primary));
background-color: var(--color-primary);
/* Create checkmark shape using clip-path */
clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
@ -2153,10 +2153,10 @@ body.initial-session-load .session-flex-responsive > session-card:nth-child(n +
/* Focus state */
.session-toggle-checkbox:focus {
outline: none;
box-shadow: 0 0 0 2px rgb(var(--color-primary) / 0.25);
box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-primary) 25%, transparent);
}
/* Focus visible for keyboard navigation */
.session-toggle-checkbox:focus-visible {
box-shadow: 0 0 0 2px rgb(var(--color-primary) / 0.4);
box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-primary) 40%, transparent);
}