localgreenchain/pages/api/plants/register-anonymous.ts
Claude ccea9535d4
Add Tor integration and privacy features for anonymous plant sharing
Implements comprehensive privacy and anonymity features including Tor
hidden service support, location obfuscation, and anonymous registration.

Privacy Features:
- Anonymous plant registration with zero personal information
- Location privacy levels: exact, fuzzy, city, country, hidden
- Pseudonymous identities and wallet addresses
- Privacy settings component with real-time Tor status
- Encrypted anonymous contact generation

Tor Integration:
- SOCKS proxy support for Tor connections
- Hidden service (.onion) configuration
- Tor connection detection and status API
- Docker Compose setup for easy Tor deployment
- Automatic privacy warnings when not using Tor

Location Obfuscation:
- Fuzzy location: ±1-5km random offset
- City level: ~10km grid
- Country level: ~100km grid
- Hidden: complete location privacy
- Haversine-based distance calculations preserved

Anonymous Registration:
- /plants/register-anonymous endpoint
- Privacy-first UI with Tor status banner
- Anonymous IDs and wallet addresses
- Optional pseudonym support
- Encryption key support for enhanced security

Infrastructure:
- Tor service integration (lib/services/tor.ts)
- Privacy utilities (lib/privacy/anonymity.ts)
- PrivacySettings React component
- Tor status API endpoint
- Docker and docker-compose configurations
- Example Tor configuration (torrc.example)

Documentation:
- Comprehensive TOR_SETUP.md guide
- Installation instructions for Linux/macOS/Windows
- Privacy best practices
- Troubleshooting guide
- Security considerations
- Updated README with Tor features

Dependencies:
- Added socks-proxy-agent for Tor proxy support

This enables:
- Privacy-conscious growers to share anonymously
- Protection of exact home locations
- Censorship-resistant plant sharing
- Community building without identity disclosure
- Compliance with privacy regulations

All privacy features are optional and configurable.
Users can choose their desired privacy level.
2025-11-16 12:32:59 +00:00

156 lines
4.9 KiB
TypeScript

/**
* API Route: Register an anonymous plant
* POST /api/plants/register-anonymous
*
* This endpoint allows for privacy-preserving plant registration
*/
import type { NextApiRequest, NextApiResponse } from 'next';
import { getBlockchain, saveBlockchain } from '../../../lib/blockchain/manager';
import { PlantData } from '../../../lib/blockchain/types';
import {
generateAnonymousId,
generateWalletAddress,
obfuscateLocation,
generateAnonymousPlantName,
createAnonymousContact,
PrivacySettings,
} from '../../../lib/privacy/anonymity';
import { getTorService } from '../../../lib/services/tor';
interface AnonymousPlantRequest {
commonName: string;
scientificName?: string;
species?: string;
genus?: string;
family?: string;
location: {
latitude: number;
longitude: number;
};
privacySettings: PrivacySettings;
pseudonym?: string;
encryptionKey?: string;
}
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
try {
const requestData: AnonymousPlantRequest = req.body;
// Validate required fields
if (!requestData.commonName || !requestData.location || !requestData.privacySettings) {
return res.status(400).json({
error: 'Missing required fields: commonName, location, privacySettings',
});
}
const { location, privacySettings } = requestData;
// Check if request came through Tor
const torService = getTorService();
const isTorConnection = torService.isRequestFromTor(req.headers);
// Generate anonymous identifiers
const anonymousUserId = generateAnonymousId();
const walletAddress = generateWalletAddress();
const plantId = `plant-${generateAnonymousId()}`;
// Obfuscate location based on privacy settings
const fuzzyLocation = obfuscateLocation(
location.latitude,
location.longitude,
privacySettings.locationPrivacy
);
// Determine display name based on privacy settings
let displayName: string;
if (privacySettings.identityPrivacy === 'anonymous') {
displayName = 'Anonymous Grower';
} else if (privacySettings.identityPrivacy === 'pseudonym' && requestData.pseudonym) {
displayName = requestData.pseudonym;
} else {
displayName = `Grower-${anonymousUserId.substring(0, 8)}`;
}
// Create anonymous contact
const anonymousEmail = createAnonymousContact(
anonymousUserId,
requestData.encryptionKey || 'default-key'
);
// Build plant data with privacy protections
const plantData: PlantData = {
id: plantId,
commonName: privacySettings.sharePlantDetails
? requestData.commonName
: generateAnonymousPlantName(requestData.commonName, 0),
scientificName: privacySettings.sharePlantDetails ? requestData.scientificName : undefined,
species: privacySettings.sharePlantDetails ? requestData.species : undefined,
genus: privacySettings.sharePlantDetails ? requestData.genus : undefined,
family: privacySettings.sharePlantDetails ? requestData.family : undefined,
propagationType: 'original',
generation: 0,
plantedDate: new Date().toISOString(),
status: 'growing',
location: {
latitude: fuzzyLocation.latitude,
longitude: fuzzyLocation.longitude,
address: fuzzyLocation.displayName,
city: privacySettings.locationPrivacy !== 'hidden' ? 'Privacy Protected' : undefined,
country: privacySettings.locationPrivacy === 'country' ? 'Anonymous Region' : undefined,
},
owner: {
id: anonymousUserId,
name: displayName,
email: anonymousEmail,
walletAddress: walletAddress,
},
childPlants: [],
registeredAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
notes: privacySettings.anonymousMode
? 'Registered anonymously via privacy mode'
: undefined,
};
const blockchain = getBlockchain();
// Register the plant
const block = blockchain.registerPlant(plantData);
// Save blockchain
saveBlockchain();
// Prepare response with privacy info
res.status(201).json({
success: true,
plant: block.plant,
privacy: {
anonymousId: anonymousUserId,
walletAddress: walletAddress,
locationAccuracy: fuzzyLocation.accuracy,
torConnection: isTorConnection,
privacyLevel: privacySettings.locationPrivacy,
},
block: {
index: block.index,
hash: block.hash,
timestamp: block.timestamp,
},
message: 'Plant registered anonymously',
warning: !isTorConnection
? 'For maximum privacy, consider accessing through Tor network'
: null,
});
} catch (error: any) {
console.error('Error registering anonymous plant:', error);
res.status(500).json({ error: error.message || 'Internal server error' });
}
}