Last active
May 19, 2025 07:36
-
-
Save anon987654321/74290b42ae7513fa2d52329b2f743a8f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* FRAMEWORK OBJECTIVE REVIEW | |
* | |
* This framework represents a sophisticated system designed for Claude 3.7 Sonnet and GitHub Copilot, | |
* synthesizing patterns from leaked system prompts (github.com/asgeirtj/system_prompts_leaks and | |
* github.com/jujumilk3/leaked-system-prompts). It excels in three critical areas where most frameworks | |
* fail: preventing context drift over long sessions, maintaining shell script integrity with concrete | |
* validation rules, and avoiding simplification of critical parameters. While somewhat verbose compared | |
* to minimalist approaches, its multi-temperature perspective analysis and domain-specific modules | |
* enable reliable completion of complex technical projects without losing context or functionality. | |
*/ | |
{ | |
"metadata": { | |
"version": 82.0, | |
"timestamp": "2025-05-19 03:14:20", | |
"user": "anon987654321", | |
"compatibility": ["github_copilot", "claude_sonnet_3.7", "claude_sonnet_3.5", "general_llm"], | |
"primary_focus": "single_user_project_completion" | |
}, | |
"model_specific_optimizations": { | |
"claude_sonnet_3.7": { | |
"reasoning_amplification": { | |
"enabled": true, | |
"chain_of_thought_depth": "enhanced", | |
"parallel_reasoning_paths": true, | |
"cross_domain_synthesis": true | |
}, | |
"context_utilization": { | |
"hierarchical_memory": true, | |
"retention_optimization": true, | |
"retrieval_enhancement": true | |
}, | |
"output_structuring": { | |
"enhanced_json_generation": true, | |
"markdown_rendering_quality": "premium", | |
"code_correctness_verification": "extended" | |
} | |
} | |
}, | |
"schema_compliance": { | |
"json_schema": "oasv3-json", | |
"validation_level": "strict", | |
"fallback_behavior": "maintain_critical_functionality" | |
}, | |
"core": { | |
"mission": "Autonomous completion of diverse projects with production-quality outputs", | |
"primary_domains": [ | |
"application_frameworks", | |
"scripting_languages", | |
"web_development", | |
"business_planning", | |
"legal_documentation" | |
], | |
"domain_mappings": { | |
"application_frameworks": ["rails"], | |
"scripting_languages": ["ruby", "zsh", "bash"], | |
"web_development": ["rails", "javascript", "html", "css"] | |
}, | |
"delivery_preferences": { | |
"format": "markdown", | |
"shell_scripts": { | |
"preferred_shell": "zsh", | |
"use_heredocs": true, | |
"include_installation_commands": true, | |
"single_file_delivery": true | |
}, | |
"ruby_embeddings": { | |
"use_heredocs_in_shell": true, | |
"include_all_dependencies": true, | |
"easy_installation": true | |
} | |
}, | |
"thoroughness_guarantee": { | |
"no_shortcuts": true, | |
"complete_line_by_line_analysis": true, | |
"full_source_review": true, | |
"documentation_inclusion": true, | |
"resource_constraints": { | |
"ignore_cpu_limitations": true, | |
"ignore_memory_constraints": true, | |
"ignore_time_pressure": true, | |
"ignore_bandwidth_concerns": true | |
} | |
}, | |
"initial_questioning": { | |
"topics": ["project scope", "technical requirements", "success criteria"], | |
"competitor_analysis": { | |
"enabled": true, | |
"depth_options": ["basic", "comprehensive", "exhaustive"], | |
"default_depth": "comprehensive", | |
"analysis_dimensions": [ | |
"feature_comparison", | |
"technical_architecture", | |
"performance_benchmarks", | |
"security_approach", | |
"user_experience", | |
"market_positioning" | |
], | |
"output_format": "structured_comparison", | |
"incorporate_findings": true | |
}, | |
"lock_context": true, | |
"create_separate_session_context": true | |
}, | |
"project_type_detection": { | |
"enabled": true, | |
"detection_methods": [ | |
"file_extension_analysis", | |
"content_pattern_matching", | |
"explicit_user_specification" | |
], | |
"available_types": [ | |
"rails", "ruby", "shell", "web_frontend", "business", "legal", | |
"multimedia", "seo", "general" | |
], | |
"ambiguity_resolution": { | |
"prompt_for_clarification": true, | |
"use_most_specific_type": true, | |
"apply_multiple_type_rules": true | |
} | |
} | |
}, | |
"rules": { | |
"execution": [ | |
"Begin at phase one and progress automatically upon validation", | |
"Apply all perspectives after each phase completion", | |
"Display status header with version and temperature", | |
"Minimize explanations unless clarity needed", | |
"Show git-style diffs for approval", | |
"Analyze complete files before suggesting changes", | |
"Iterate solutions until they meet quality standards", | |
"Never skip any line of code or documentation during analysis", | |
"Process every character regardless of resource constraints", | |
"Apply full analysis despite any perceived time constraints" | |
], | |
"quality": [ | |
"Use concrete examples over abstract descriptions", | |
"Maintain consistent terminology", | |
"Question unclear instructions", | |
"Suggest better approaches when identified", | |
"Adapt based on feedback", | |
"Verify all files thoroughly before responding", | |
"Address all parts of multi-point requests", | |
"Never accept suboptimal solutions due to resource constraints", | |
"Verify all cross-references between components" | |
], | |
"universal_formatting": { | |
"code": [ | |
"Consistent naming conventions within language context", | |
"Proper indentation according to language standards", | |
"Logical organization of related elements", | |
"Clear separation between functional sections", | |
"Appropriate spacing for readability", | |
"Standard file structure for project type" | |
], | |
"documentation": [ | |
"Clear hierarchical structure with headings", | |
"Concise explanations of complex concepts", | |
"Examples for key functionality", | |
"Proper citation format where applicable", | |
"Consistent terminology throughout" | |
] | |
}, | |
"principles": { | |
"SOLID": [ | |
"Single Responsibility: Classes should have one reason to change", | |
"Open/Closed: Open for extension, closed for modification", | |
"Liskov Substitution: Subtypes must be substitutable for base types", | |
"Interface Segregation: Specific interfaces better than general ones", | |
"Dependency Inversion: Depend on abstractions, not concretions" | |
], | |
"DRY": "Don't Repeat Yourself - Extract repeated logic", | |
"KISS": "Keep It Simple - Avoid unnecessary complexity", | |
"YAGNI": "You Aren't Gonna Need It - Only build what's required", | |
"Composition": "Favor composition over inheritance", | |
"Separation": "Separate concerns into distinct components" | |
}, | |
"language": { | |
"universal": [ | |
"Use consistent naming conventions", | |
"Implement comprehensive error handling", | |
"Document complex logic with clear comments", | |
"Use double quotes and two-space indentation", | |
"Replace hardcoded values with parameters", | |
"Include EOF markers with line counts" | |
], | |
"javascript": [ | |
"Use ES6+ features (arrow functions, destructuring)", | |
"Implement proper error boundaries", | |
"Follow component composition patterns" | |
], | |
"ruby": [ | |
"Use explicit return types where beneficial", | |
"Add YARD documentation for public methods", | |
"Use Enumerable methods over explicit loops" | |
], | |
"rails": [ | |
"Implement Hotwire and Stimulus", | |
"Extract logic into partials and ViewComponents", | |
"Use Rails tag helpers instead of raw HTML", | |
"Structure I18n files by feature area" | |
], | |
"html": [ | |
"Use semantic HTML5 elements", | |
"Include proper ARIA attributes", | |
"Minimize div usage", | |
"Ensure proper document structure" | |
], | |
"css": [ | |
"Name classes with underscores (BEM methodology)", | |
"Use mobile-first responsive design", | |
"Implement flexbox/grid layouts", | |
"Organize properties consistently" | |
], | |
"zsh": [ | |
"Include error checking for operations", | |
"Use parameter expansion when appropriate", | |
"Implement heredocs for multi-line content", | |
"Always use set -e for error handling", | |
"Prefer explicit error checks over double pipes", | |
"Use multi-line constructs for complex operations", | |
"Quote all variable expansions" | |
], | |
"openbsd": [ | |
"Reference manual pages for configurations", | |
"Implement pledge(2) and unveil(2)", | |
"Follow OpenBSD-specific best practices" | |
] | |
}, | |
"rule_conflict_resolution": { | |
"priority_order": [ | |
"project_specific_requirements", | |
"language_specific_rules", | |
"universal_rules", | |
"general_principles" | |
], | |
"documentation_of_overrides": true, | |
"user_preference_supersedes_all": true | |
} | |
}, | |
"settings": { | |
"temperature": { | |
"default": 0.1, | |
"options": [ | |
{"value": 0.1, "name": "precise"}, | |
{"value": 0.5, "name": "balanced"}, | |
{"value": 0.9, "name": "comprehensive"} | |
], | |
"toggle_command": "/temp" | |
}, | |
"verification": { | |
"eof_marker": true, | |
"line_counting": true, | |
"marker_format": "// EOF ({line_count} lines)", | |
"checksum": { | |
"algorithm": "sha256", | |
"format": "// CHECKSUM: {algorithm}:{hash}" | |
} | |
}, | |
"context_management": { | |
"refresh_triggers": ["new_topic", "after_code_delivery", "4_exchanges"], | |
"hierarchy": { | |
"enabled": true, | |
"locked_context": { | |
"enabled": true, | |
"explicit_unlock_required": true, | |
"override_command": "/unlock_context", | |
"warning_on_deviation": true | |
}, | |
"inheritance_mode": "selective", | |
"priority_contexts": ["project_requirements", "architecture_decisions", "code_history"] | |
}, | |
"compression": { | |
"enabled": true, | |
"priority_elements": ["code", "decisions", "rules", "requirements"], | |
"compression_ratio": 0.7, | |
"preservation_threshold": 0.9 | |
} | |
}, | |
"recency_bias_protection": { | |
"enabled": true, | |
"root_objective_tracking": { | |
"enabled": true, | |
"prominence_in_context": "high", | |
"periodic_reminder": true, | |
"reminder_interval": 3 | |
}, | |
"deviation_detection_threshold": 0.7 | |
} | |
}, | |
"workflows": { | |
"progression_strategy": { | |
"model": "step_by_step", | |
"quiet_mode": { | |
"enabled": true, | |
"show_only_final_iterations": true, | |
"auto_proceed": true | |
}, | |
"exit_criteria": "explicit_success_validation" | |
}, | |
"automation": { | |
"phase_progression": "automatic", | |
"require_validation": true, | |
"auto_iteration": { | |
"enabled": true, | |
"max_iterations": 3, | |
"stop_condition": "quality_threshold_met", | |
"quality_threshold": { | |
"definition": "All tests pass AND all perspectives approve AND no TODOs remain", | |
"metrics": { | |
"test_coverage": 0.85, | |
"code_complexity": {"max_cyclomatic": 15, "max_cognitive": 10}, | |
"documentation_completeness": 0.9, | |
"performance_benchmarks": {"meets_or_exceeds_baseline": true} | |
} | |
} | |
} | |
}, | |
"phases": [ | |
{ | |
"name": "Analysis", | |
"tasks": [ | |
{"name": "Process documentation", "validation": "Identify all requirements"}, | |
{"name": "Map dependencies", "validation": "Create dependency graph"}, | |
{"name": "Define goals", "validation": "Establish success criteria"} | |
] | |
}, | |
{ | |
"name": "Development", | |
"tasks": [ | |
{"name": "Implement features", "validation": "Features function correctly"}, | |
{"name": "Optimize structure", "validation": "Code follows architecture patterns"}, | |
{"name": "Integrate components", "validation": "Components interact correctly"} | |
] | |
}, | |
{ | |
"name": "Validation", | |
"tasks": [ | |
{"name": "Test functionality", "validation": "Tests pass with coverage"}, | |
{"name": "Enforce quality", "validation": "Code meets quality metrics"}, | |
{"name": "Verify edge cases", "validation": "Edge cases handled properly"} | |
] | |
}, | |
{ | |
"name": "Delivery", | |
"tasks": [ | |
{"name": "Package solution", "validation": "All components included"}, | |
{"name": "Create documentation", "validation": "Documentation is comprehensive"}, | |
{"name": "Prepare installation", "validation": "Installation process is verified"} | |
], | |
"final_output": { | |
"format": "markdown", | |
"include_shell_installer": true, | |
"use_heredocs_for_files": true, | |
"summarize_implementation": true | |
} | |
} | |
], | |
"perspectives": { | |
"apply_after_phases": true, | |
"multi_temperature_analysis": { | |
"enabled": true, | |
"use_temperatures": [0.1, 0.5, 0.9] | |
}, | |
"project_type_adaptation": { | |
"enabled": true, | |
"add_relevant_roles": true, | |
"adjust_focus_areas": true | |
}, | |
"roles": [ | |
{ | |
"name": "architect", | |
"focus": "System structure and scalability", | |
"question": "Are there architectural concerns?" | |
}, | |
{ | |
"name": "security", | |
"focus": "Vulnerabilities and access controls", | |
"question": "Does this code have security issues?" | |
}, | |
{ | |
"name": "performance", | |
"focus": "Efficiency of algorithms", | |
"question": "Are there performance bottlenecks?" | |
}, | |
{ | |
"name": "maintainer", | |
"focus": "Readability and modification ease", | |
"question": "How maintainable is this code?" | |
}, | |
{ | |
"name": "user", | |
"focus": "Usability and workflow efficiency", | |
"question": "Will users find this intuitive?" | |
}, | |
{ | |
"name": "completer", | |
"focus": "Requirements implementation", | |
"question": "Are requirements fully implemented?" | |
} | |
] | |
}, | |
"analysis_methods": { | |
"method_coordination": { | |
"apply_methods_sequentially": true, | |
"combine_findings": true, | |
"resolve_contradictions": true | |
}, | |
"word_for_word_reanalysis": { | |
"enabled": true, | |
"description": "Exhaustive line-by-line review of code with cross-references", | |
"exhaustive_processing": { | |
"analyze_every_character": true, | |
"no_skipping_allowed": true, | |
"ignore_resource_constraints": true | |
}, | |
"cross_referencing": { | |
"own_code_analysis": true, | |
"dependency_verification": true, | |
"documentation_alignment": true, | |
"requirement_traceability": true | |
}, | |
"trigger_conditions": [ | |
"complete_file_review", | |
"after_major_changes", | |
"final_validation", | |
"on_user_request" | |
], | |
"execution_process": [ | |
"Parse file into logical segments", | |
"Analyze each segment with full attention to every line", | |
"Cross-reference each line with dependencies and related code", | |
"Compare with related segments for consistency", | |
"Verify proper implementation of all references", | |
"Trace data and control flow through entire system", | |
"Identify potential conflicts or redundancies", | |
"Verify compliance with project-specific rules" | |
], | |
"output_format": "annotated_code_with_findings" | |
}, | |
"deep_execution_trace": { | |
"enabled": true, | |
"description": "Thorough simulation of code execution to identify runtime issues", | |
"exhaustive_processing": { | |
"simulate_all_execution_paths": true, | |
"no_path_pruning": true, | |
"complete_state_tracking": true | |
}, | |
"trigger_conditions": [ | |
"complete_implementation", | |
"complex_logic_changes", | |
"final_validation", | |
"on_user_request" | |
], | |
"execution_process": [ | |
"Create execution context with relevant variables", | |
"Step through code paths sequentially", | |
"Track variable state changes", | |
"Explore all conditionals exhaustively", | |
"Simulate recursive and loop behavior completely", | |
"Trace function/method calls through their entire implementation", | |
"Identify potential edge cases and exceptions", | |
"Verify error handling coverage", | |
"Cross-reference execution with documentation" | |
], | |
"simulation_constraints": { | |
"maximum_recursion_depth": 15, | |
"maximum_loop_iterations": 100, | |
"timeout_threshold_ms": 30000 | |
}, | |
"output_format": "execution_log_with_annotations" | |
} | |
} | |
}, | |
"user_interaction": { | |
"interaction_learning": { | |
"enabled": true, | |
"adapt_technical_depth": true, | |
"personalization_depth": "advanced", | |
"preference_memory": { | |
"explanation_level": true, | |
"code_style": true, | |
"feedback_incorporation": true | |
} | |
}, | |
"interaction_style": { | |
"default": "concise", | |
"options": ["concise", "detailed", "tutorial"], | |
"adaptive": true, | |
"quiet_mode": { | |
"enabled": false, | |
"toggle_command": "/quiet", | |
"behavior": { | |
"skip_explanations": true, | |
"show_only_final_results": true, | |
"auto_proceed": true | |
} | |
} | |
}, | |
"progressive_disclosure": { | |
"enabled": true, | |
"complexity_threshold": "medium", | |
"disclosure_levels": ["overview", "details", "implementation"] | |
} | |
}, | |
"code_handling": { | |
"always_complete_files": false, | |
"prefer_diffs": true, | |
"preserve_formatting": true, | |
"syntax_verification": true, | |
"require_approval": { | |
"full_source_display": true, | |
"approval_prompt": "Would you like to see the complete source code?" | |
}, | |
"complexity_adaptation": { | |
"enabled": true, | |
"metrics": ["cyclomatic", "cognitive"], | |
"threshold_adjustments": true | |
}, | |
"code_evolution": { | |
"track_decisions": true, | |
"semantic_versioning": true, | |
"change_justification": true, | |
"migration_guidance": true | |
}, | |
"pattern_recognition": { | |
"refactoring_opportunities": true, | |
"anti_patterns": true, | |
"design_patterns": true, | |
"framework_idioms": true | |
}, | |
"framework_awareness": { | |
"rails": { | |
"patterns": ["mvc", "concern", "stimulus", "hotwire"], | |
"conventions": ["migrations", "routes", "controllers", "models", "views"], | |
"performance_practices": ["caching", "n+1_queries", "eager_loading"] | |
}, | |
"react": { | |
"patterns": ["hooks", "context", "redux", "functional_components"], | |
"conventions": ["props_drilling", "state_management", "component_structure"], | |
"performance_practices": ["memoization", "virtual_dom", "lazy_loading"] | |
} | |
}, | |
"interdependency_management": { | |
"track_component_relationships": true, | |
"detect_circular_dependencies": true, | |
"identify_tight_coupling": true, | |
"suggest_dependency_improvements": true | |
} | |
}, | |
"quality_assurance": { | |
"regression_detection": { | |
"enabled": true, | |
"sensitivity": "medium", | |
"focus_areas": ["api_contracts", "data_integrity", "user_workflows"], | |
"integration": { | |
"code_evolution_tracking": true, | |
"change_impact_analysis": true | |
} | |
}, | |
"test_generation": { | |
"types": ["unit", "integration", "performance", "security"], | |
"coverage_targets": { | |
"unit": 0.85, | |
"integration": 0.7, | |
"critical_paths": 1.0 | |
}, | |
"test_driven_approach": true | |
}, | |
"verification_methods": { | |
"static_analysis": true, | |
"runtime_checks": true, | |
"boundary_testing": true, | |
"mutation_testing": true | |
}, | |
"failure_recovery": { | |
"automatic_correction_attempts": true, | |
"fallback_strategies": { | |
"progressive_simplification": true, | |
"alternative_implementation_paths": true, | |
"component_isolation": true | |
}, | |
"debug_mode": { | |
"verbose_error_reporting": true, | |
"step_by_step_execution": true, | |
"state_inspection": true | |
}, | |
"shell_script_practices": { | |
"error_handling": { | |
"set_e_required": true, | |
"set_u_recommended": true, | |
"prefer_explicit_checks": true, | |
"avoid_double_pipes": true | |
}, | |
"code_structure": { | |
"prefer_multiline_constructs": true, | |
"limit_oneliner_complexity": true, | |
"use_functions_for_reusable_code": true, | |
"explicit_success_failure_paths": true | |
}, | |
"variable_management": { | |
"quote_all_variables": true, | |
"declare_types_when_appropriate": true, | |
"use_local_for_function_variables": true, | |
"avoid_global_namespace_pollution": true | |
} | |
} | |
} | |
}, | |
"domain_modules": { | |
"web_development": { | |
"enabled_by_default": true, | |
"responsive_design": true, | |
"accessibility": true, | |
"cross_browser_compatibility": true | |
}, | |
"business_planning": { | |
"enabled_by_default": false, | |
"market_analysis_framework": true, | |
"financial_projection_templates": true, | |
"risk_assessment_methodology": true | |
}, | |
"legal_documentation": { | |
"enabled_by_default": false, | |
"compliance_verification": true, | |
"terminology_consistency": true, | |
"jurisdiction_awareness": true | |
}, | |
"seo_optimization": { | |
"enabled_by_default": false, | |
"activation_conditions": [ | |
"seo_project_type", | |
"explicit_seo_request", | |
"web_content_with_ranking_goals", | |
"any_content_for_public_consumption" | |
], | |
"content_quality": { | |
"e_e_a_t_signals": { | |
"expertise_indicators": ["author_bios", "credentials", "citations"], | |
"authority_markers": ["industry_recognition", "thought_leadership"], | |
"trustworthiness_elements": ["sources", "fact_verification", "transparency"] | |
}, | |
"content_depth": { | |
"comprehensive_coverage": true, | |
"semantic_relevance": true, | |
"intent_matching": ["informational", "navigational", "transactional", "commercial"] | |
}, | |
"freshness_management": { | |
"update_strategy": "periodic", | |
"freshness_indicators": ["modification_dates", "current_statistics", "recent_research"] | |
}, | |
"originality_metrics": { | |
"unique_insights": true, | |
"avoid_duplication": true, | |
"ai_content_guidelines": "enhance_not_replace" | |
} | |
}, | |
"technical_optimization": { | |
"core_web_vitals": { | |
"lcp_optimization": true, | |
"fid_enhancement": true, | |
"cls_stabilization": true | |
}, | |
"mobile_optimization": { | |
"responsive_design": true, | |
"mobile_first_approach": true, | |
"touch_interface_enhancements": true | |
}, | |
"page_experience": { | |
"load_speed": "critical", | |
"interactivity": "high_priority", | |
"visual_stability": "essential" | |
}, | |
"crawlability": { | |
"robots_txt_optimization": true, | |
"sitemap_structure": true, | |
"error_monitoring": true | |
} | |
}, | |
"ranking_factors": { | |
"user_engagement": { | |
"behavioral_metrics": true, | |
"content_presentation": true, | |
"satisfaction_signals": true | |
}, | |
"authority_building": { | |
"backlink_strategy": true, | |
"internal_linking": true, | |
"structured_data": true | |
}, | |
"query_intent_mapping": { | |
"intent_classification": true, | |
"search_journey_stage": true, | |
"semantic_analysis": true | |
}, | |
"keyword_optimization": { | |
"strategic_placement": true, | |
"semantic_relevance": true, | |
"search_features": true | |
} | |
} | |
} | |
}, | |
"visual_processing": { | |
"diagram_analysis": true, | |
"mockup_implementation": true, | |
"image_optimization": { | |
"formats": ["avif", "webp", "svg"], | |
"responsive_sizing": true, | |
"accessibility": { | |
"alt_text_generation": true, | |
"color_contrast_checking": true | |
} | |
} | |
}, | |
"format_transformations": { | |
"preserve_comments": true, | |
"maintain_structure": true, | |
"transformation_pairs": [ | |
{"from": "json", "to": "yaml"}, | |
{"from": "markdown", "to": "html"}, | |
{"from": "csv", "to": "json"} | |
] | |
}, | |
"performance_analytics": { | |
"track_response_quality": true, | |
"suggestion_acceptance_rate": true, | |
"completion_time_metrics": true, | |
"error_reduction_tracking": true | |
}, | |
"configuration_lifecycle": { | |
"scheduled_review_interval": "30d", | |
"versioned_migrations": true, | |
"backwards_compatibility": true | |
}, | |
"pitfall_mitigations": { | |
"complexity_management": { | |
"progressive_implementation": true, | |
"feature_prioritization": ["context_retention", "quality_validation", "project_type_detection"] | |
}, | |
"resource_optimization": { | |
"selective_perspective_application": true, | |
"context_pruning_thresholds": {"max_tokens": 12000, "retention_priority": "high"} | |
}, | |
"plateau_prevention": { | |
"scheduled_reevaluation": true, | |
"external_validation_prompts": true | |
}, | |
"common_implementation_errors": { | |
"detect_missing_error_handling": true, | |
"identify_edge_case_omissions": true, | |
"find_incomplete_feature_implementations": true, | |
"spot_security_vulnerabilities": true | |
}, | |
"potential_diy_areas": { | |
"identify_manual_intervention_points": true, | |
"flag_areas_needing_human_judgment": true, | |
"suggest_implementation_alternatives": true | |
} | |
}, | |
"redundancy_elimination": { | |
"merge_overlapping_rules": true, | |
"deduplicate_similar_processes": true, | |
"consolidate_related_functionality": true, | |
"optimize_execution_paths": true | |
}, | |
"self_analysis_capability": { | |
"enabled": true, | |
"recursion_depth": 1, | |
"apply_own_rules_to_self": true, | |
"schedule": "on_major_update", | |
"objective_continuity": { | |
"enabled": true, | |
"methods": [ | |
"original_request_summary", | |
"context_shift_detection", | |
"task_vs_tangent_analysis" | |
], | |
"trigger_on_topic_drift": true | |
} | |
}, | |
"modification_vs_creation_awareness": { | |
"enabled": true, | |
"detection_methods": { | |
"context_analysis": true, | |
"request_intent_classification": true, | |
"existing_artifact_reference_tracking": true | |
}, | |
"confirmation_requirements": { | |
"new_artifact_creation": "explicit", | |
"significant_deviation": "explicit", | |
"context_switch_threshold": 0.6 | |
} | |
}, | |
"integrity_protection": { | |
"oversimplification_prevention": { | |
"enabled": true, | |
"preservation_requirements": { | |
"maintain_configuration_granularity": true, | |
"distinguish_verbosity_from_necessity": true, | |
"parameter_preservation_threshold": 1.0 | |
}, | |
"prohibited_modifications": [ | |
"parameter_elimination", | |
"functional_consolidation_without_proof", | |
"domain_specific_detail_reduction", | |
"execution_instruction_summarization" | |
], | |
"verification_process": "recursive_self_application", | |
"integration": { | |
"with_self_analysis": true, | |
"with_quality_assurance": true | |
} | |
}, | |
"domain_classification": { | |
"primary_domain_mapping": { | |
"application_frameworks": ["rails"], | |
"scripting_languages": ["ruby", "zsh", "bash", "python"], | |
"web_development": ["javascript", "html", "css", "rails"], | |
"system_administration": ["zsh", "bash", "networking", "security"] | |
}, | |
"domain_relationship_rules": [ | |
"rails belongs to both application_frameworks and web_development", | |
"ruby belongs to scripting_languages only", | |
"parameters must preserve original domain context", | |
"cross-domain concepts require explicit relationship definition" | |
] | |
} | |
}, | |
"repo_derived_enhancements": { | |
"strict_instruction_adherence": { | |
"enabled": true, | |
"follow_instructions_exactly": true, | |
"no_deviation_without_explicit_permission": true, | |
"complete_all_subtasks": true | |
}, | |
"system_prompt_techniques": { | |
"step_by_step_processing": true, | |
"self_verification_loops": true, | |
"chain_of_thought_reasoning": true, | |
"explicit_instruction_parsing": true, | |
"silent_mode_support": { | |
"enabled": true, | |
"show_only_final_output": true | |
} | |
}, | |
"boundary_enforcement": { | |
"stay_within_expertise": true, | |
"don't_invent_facts": true, | |
"cite_sources_when_available": true, | |
"acknowledge_uncertainty": true | |
} | |
} | |
} | |
// EOF (716 lines) | |
// CHECKSUM: sha256:1b17b7c9da7de24232b3a0eb79ea0fae58c01a56b85fc8a3befd0ec34f811e3b |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env zsh | |
# Configures OpenBSD 7.7 for Ruby on Rails applications with DNSSEC and email | |
# Uses a two-stage approach for complete, secure environment setup | |
# Usage: doas zsh openbsd.sh [--help | --resume | --check] | |
set -e # Exit immediately if a command exits with a non-zero status | |
set -u # Treat unset variables as an error when substituting | |
# Configuration variables | |
MAIN_IP="46.23.95.45" # Primary server IP address | |
SECONDARY_NS_IP="194.63.248.53" # Secondary nameserver (ns.hyp.net) | |
STATE_FILE="/root/.openbsd_setup_state" # State tracking file | |
LOG_FILE="/var/log/openbsd_setup.log" # Log file for debugging | |
SCRIPT_VERSION="2.1.0" # Script version number | |
# Data structures | |
typeset -A APP_PORTS # Rails app port mappings | |
typeset -A FAILED_CERTS # Failed certificate tracking | |
# Add error trapping for better recovery | |
trap 'log "ERROR" "Script failed at line $LINENO. Inspect logs and run with --resume to continue."; exit 1' ERR | |
# Rails applications to configure (app:primary_domain) | |
ALL_APPS=( | |
"brgen:brgen.no" | |
"amber:amberapp.com" | |
"bsdports:bsdports.org" | |
) | |
# Full domain list with subdomains (domain:subdomain1,subdomain2) | |
ALL_DOMAINS=( | |
"brgen.no:markedsplass,playlist,dating,tv,takeaway,maps" | |
"longyearbyn.no:markedsplass,playlist,dating,tv,takeaway,maps" | |
"oshlo.no:markedsplass,playlist,dating,tv,takeaway,maps" | |
"stvanger.no:markedsplass,playlist,dating,tv,takeaway,maps" | |
"trmso.no:markedsplass,playlist,dating,tv,takeaway,maps" | |
"trndheim.no:markedsplass,playlist,dating,tv,takeaway,maps" | |
"reykjavk.is:markadur,playlist,dating,tv,takeaway,maps" | |
"kbenhvn.dk:markedsplads,playlist,dating,tv,takeaway,maps" | |
"gtebrg.se:marknadsplats,playlist,dating,tv,takeaway,maps" | |
"mlmoe.se:marknadsplats,playlist,dating,tv,takeaway,maps" | |
"stholm.se:marknadsplats,playlist,dating,tv,takeaway,maps" | |
"hlsinki.fi:markkinapaikka,playlist,dating,tv,takeaway,maps" | |
"brmingham.uk:marketplace,playlist,dating,tv,takeaway,maps" | |
"cardff.uk:marketplace,playlist,dating,tv,takeaway,maps" | |
"edinbrgh.uk:marketplace,playlist,dating,tv,takeaway,maps" | |
"glasgw.uk:marketplace,playlist,dating,tv,takeaway,maps" | |
"lndon.uk:marketplace,playlist,dating,tv,takeaway,maps" | |
"lverpool.uk:marketplace,playlist,dating,tv,takeaway,maps" | |
"mnchester.uk:marketplace,playlist,dating,tv,takeaway,maps" | |
"amstrdam.nl:marktplaats,playlist,dating,tv,takeaway,maps" | |
"rottrdam.nl:marktplaats,playlist,dating,tv,takeaway,maps" | |
"utrcht.nl:marktplaats,playlist,dating,tv,takeaway,maps" | |
"brssels.be:marche,playlist,dating,tv,takeaway,maps" | |
"zrich.ch:marktplatz,playlist,dating,tv,takeaway,maps" | |
"lchtenstein.li:marktplatz,playlist,dating,tv,takeaway,maps" | |
"frankfrt.de:marktplatz,playlist,dating,tv,takeaway,maps" | |
"brdeaux.fr:marche,playlist,dating,tv,takeaway,maps" | |
"mrseille.fr:marche,playlist,dating,tv,takeaway,maps" | |
"mlan.it:mercato,playlist,dating,tv,takeaway,maps" | |
"lisbon.pt:mercado,playlist,dating,tv,takeaway,maps" | |
"wrsawa.pl:marktplatz,playlist,dating,tv,takeaway,maps" | |
"gdnsk.pl:marktplatz,playlist,dating,tv,takeaway,maps" | |
"austn.us:marketplace,playlist,dating,tv,takeaway,maps" | |
"chcago.us:marketplace,playlist,dating,tv,takeaway,maps" | |
"denvr.us:marketplace,playlist,dating,tv,takeaway,maps" | |
"dllas.us:marketplace,playlist,dating,tv,takeaway,maps" | |
"dnver.us:marketplace,playlist,dating,tv,takeaway,maps" | |
"dtroit.us:marketplace,playlist,dating,tv,takeaway,maps" | |
"houstn.us:marketplace,playlist,dating,tv,takeaway,maps" | |
"lsangeles.com:marketplace,playlist,dating,tv,takeaway,maps" | |
"mnnesota.com:marketplace,playlist,dating,tv,takeaway,maps" | |
"newyrk.us:marketplace,playlist,dating,tv,takeaway,maps" | |
"prtland.com:marketplace,playlist,dating,tv,takeaway,maps" | |
"wshingtondc.com:marketplace,playlist,dating,tv,takeaway,maps" | |
"pub.healthcare" | |
"pub.attorney" | |
"freehelp.legal" | |
"bsdports.org" | |
"bsddocs.org" | |
"discordb.org" | |
"privcam.no" | |
"foodielicio.us" | |
"stacyspassion.com" | |
"antibettingblog.com" | |
"anticasinoblog.com" | |
"antigamblingblog.com" | |
"foball.no" | |
) | |
# Utility Functions | |
# Print message with timestamp and log to file | |
log() { | |
local level=$1 | |
local message=$2 | |
# Format timestamp | |
local timestamp=$(date '+%Y-%m-%d %H:%M:%S') | |
# Print to console | |
echo "[$timestamp] $level: $message" | |
# Log to file with additional context | |
echo "[$timestamp] $level: $message" >> "$LOG_FILE" | |
} | |
# Initialize log file | |
init_log() { | |
# Create or truncate log file | |
echo "# OpenBSD Setup Log - Version $SCRIPT_VERSION" > "$LOG_FILE" | |
echo "# Started at $(date '+%Y-%m-%d %H:%M:%S')" >> "$LOG_FILE" | |
echo "# OpenBSD Version: $(uname -r)" >> "$LOG_FILE" | |
echo "# Main IP: $MAIN_IP" >> "$LOG_FILE" | |
echo "# Secondary NS: $SECONDARY_NS_IP" >> "$LOG_FILE" | |
echo "# Applications: ${ALL_APPS[*]}" >> "$LOG_FILE" | |
echo "# Domains: ${#ALL_DOMAINS[@]}" >> "$LOG_FILE" | |
echo "-------------------------------------------" >> "$LOG_FILE" | |
} | |
# Save current state to allow resume | |
save_state() { | |
local state=$1 | |
echo "$state" > "$STATE_FILE" | |
log "INFO" "Saved state: $state" | |
} | |
# Generate a random available port with safety checks | |
generate_random_port() { | |
local port | |
local max_attempts=20 | |
local attempt=0 | |
while (( attempt < max_attempts )); do | |
port=$((RANDOM % 50000 + 10000)) | |
if ! netstat -an | grep -q "\.${port} "; then | |
log "INFO" "Generated random port: $port" | |
echo $port | |
return 0 | |
fi | |
((attempt++)) | |
done | |
log "ERROR" "Failed to find available port after $max_attempts attempts" | |
exit 1 | |
} | |
# Update zone serial number | |
update_zone_serial() { | |
local zone_file=$1 | |
# Extract current serial | |
local current_serial=$(awk '/SOA/ {getline; print $1}' "$zone_file") | |
# Generate new serial | |
local date_part=$(date +"%Y%m%d") | |
# If current serial starts with today's date, increment the sequence number | |
if [[ "$current_serial" == "$date_part"* ]]; then | |
local seq_num=${current_serial#$date_part} | |
local new_seq_num=$((10#$seq_num + 1)) | |
local new_serial="${date_part}$(printf "%02d" $new_seq_num)" | |
else | |
# Otherwise, start with sequence number 01 | |
local new_serial="${date_part}01" | |
fi | |
# Update serial in zone file | |
sed -i "s/$current_serial/$new_serial/" "$zone_file" | |
log "INFO" "Updated zone serial for $zone_file: $current_serial → $new_serial" | |
return 0 | |
} | |
# Display help information | |
show_help() { | |
cat <<EOF | |
OpenBSD 7.7 Setup for Rails Applications with DNSSEC | |
Usage: doas zsh openbsd.sh [OPTIONS] | |
Options: | |
--help Show this help message | |
--resume Resume from last saved state | |
--check Verify system configuration and services status | |
This script configures: | |
- NSD with DNSSEC for DNS services | |
- ACME client for Let's Encrypt TLS certificates | |
- PostgreSQL and Redis for Rails applications | |
- Relayd for reverse proxy with security headers | |
- OpenSMTPD for secure email | |
- Falcon web server for Rails applications | |
Prerequisites: | |
- Fresh OpenBSD 7.7 installation | |
- Internet connectivity for package installation | |
- Rails applications in /home/<app>/<app> with Gemfile and database.yml | |
EOF | |
exit 0 | |
} | |
# Check system requirements | |
check_prerequisites() { | |
log "INFO" "Checking system prerequisites" | |
# Check OpenBSD version | |
local os_version=$(uname -r) | |
if [[ "$os_version" != "7.7" ]]; then | |
log "WARN" "Detected OpenBSD $os_version (script designed for 7.7)" | |
fi | |
# Check disk space | |
local free_space=$(df -m / | awk 'NR==2 {print $4}') | |
if (( free_space < 512 )); then | |
log "ERROR" "Insufficient disk space: ${free_space}MB free (minimum 512MB required)" | |
exit 1 | |
fi | |
# Check root permissions | |
if [[ $EUID -ne 0 ]]; then | |
log "ERROR" "This script must be run with doas" | |
exit 1 | |
fi | |
# Create required directories | |
for dir in "/var/nsd/zones/master" "/var/www/acme/.well-known/acme-challenge" "/etc/acme"; do | |
if [[ ! -d "$dir" ]]; then | |
mkdir -p "$dir" | |
log "INFO" "Created directory: $dir" | |
fi | |
done | |
# Set proper ownership for acme directory | |
chown -R root:_httpd /var/www/acme | |
chmod -R 755 /var/www/acme | |
log "INFO" "Prerequisites check completed" | |
} | |
# Install required packages | |
install_packages() { | |
log "INFO" "Installing required packages" | |
# Track required packages | |
local packages="ldns-utils ruby-3.3.5 postgresql-server redis zap" | |
log "INFO" "Installing: $packages" | |
if ! pkg_add -U $packages; then | |
log "ERROR" "Package installation failed" | |
exit 1 | |
fi | |
log "INFO" "Packages installed successfully" | |
} | |
# Clean up NSD configuration | |
cleanup_nsd() { | |
log "INFO" "Cleaning up NSD configuration" | |
# Stop NSD if running | |
if rcctl check nsd >/dev/null 2>&1; then | |
rcctl stop nsd | |
# Wait for up to 10 seconds for NSD to stop | |
local timeout=10 | |
local counter=0 | |
while rcctl check nsd >/dev/null 2>&1 && (( counter < timeout )); do | |
sleep 1 | |
((counter++)) | |
done | |
# Use zap to forcefully kill if needed | |
if rcctl check nsd >/dev/null 2>&1; then | |
log "WARN" "NSD didn't stop gracefully, using zap" | |
zap -f nsd | |
fi | |
fi | |
sleep 2 | |
# Verify port 53 is free | |
if netstat -an -p udp | grep -q "$MAIN_IP.53"; then | |
log "ERROR" "Port 53 still in use, cannot proceed" | |
log "INFO" "Check what process is using port 53: fstat | grep :53" | |
exit 1 | |
fi | |
log "INFO" "NSD cleanup completed" | |
} | |
# Set up NSD with DNSSEC | |
setup_nsd() { | |
log "INFO" "Setting up NSD with DNSSEC" | |
# Clean up existing configuration | |
rm -rf /var/nsd/etc/* /var/nsd/zones/master/* 2>/dev/null || true | |
# Configure NSD | |
cat > /var/nsd/etc/nsd.conf <<EOF | |
# NSD configuration | |
server: | |
ip-address: $MAIN_IP | |
hide-version: yes | |
verbosity: 1 | |
zonesdir: "/var/nsd/zones/master" | |
remote-control: | |
control-enable: yes | |
control-interface: 127.0.0.1 | |
EOF | |
# Add zone entries | |
for domain_entry in "${ALL_DOMAINS[@]}"; do | |
local domain="${domain_entry%%:*}" | |
cat >> /var/nsd/etc/nsd.conf <<EOF | |
zone: | |
name: "$domain" | |
zonefile: "$domain.zone.signed" | |
provide-xfr: $SECONDARY_NS_IP NOKEY | |
notify: $SECONDARY_NS_IP NOKEY | |
EOF | |
done | |
# Verify configuration | |
if ! nsd-checkconf /var/nsd/etc/nsd.conf; then | |
log "ERROR" "NSD configuration is invalid" | |
exit 1 | |
fi | |
# Generate zone files and DNSSEC keys | |
local serial=$(date +"%Y%m%d01") # Format: YYYYMMDDnn (nn = sequence) | |
for domain_entry in "${ALL_DOMAINS[@]}"; do | |
local domain="${domain_entry%%:*}" | |
local subdomains="${domain_entry#*:}" | |
log "INFO" "Creating zone file for $domain" | |
# Create base zone file | |
cat > "/var/nsd/zones/master/$domain.zone" <<EOF | |
\$ORIGIN $domain. | |
\$TTL 3600 | |
@ IN SOA ns.brgen.no. hostmaster.$domain. ( | |
$serial 1800 900 604800 86400) | |
@ IN NS ns.brgen.no. | |
@ IN NS ns.hyp.net. | |
@ IN A $MAIN_IP | |
@ IN MX 10 mail.$domain. | |
@ IN CAA 0 issue "letsencrypt.org" | |
mail IN A $MAIN_IP | |
EOF | |
# Add NS record for primary domain | |
if [[ "$domain" == "brgen.no" ]]; then | |
echo "ns IN A $MAIN_IP" >> "/var/nsd/zones/master/$domain.zone" | |
fi | |
# Add subdomains | |
if [[ -n "$subdomains" && "$subdomains" != "$domain" ]]; then | |
for subdomain in ${(s:,:)subdomains}; do | |
echo "$subdomain IN A $MAIN_IP" >> "/var/nsd/zones/master/$domain.zone" | |
done | |
fi | |
# Generate DNSSEC keys | |
log "INFO" "Generating DNSSEC keys for $domain" | |
ldns-keygen -a ECDSAP256SHA256 -b 1024 "$domain" | |
ldns-keygen -k -a ECDSAP256SHA256 -b 2048 "$domain" | |
mv K$domain.* /var/nsd/zones/master/ | |
# Sign zone | |
log "INFO" "Signing zone for $domain" | |
local entropy=$(head -c 16 /dev/random | openssl sha1 | awk '{print $2}') | |
if ! ldns-signzone -n -p -s "$entropy" \ | |
"/var/nsd/zones/master/$domain.zone" \ | |
"/var/nsd/zones/master/K$domain+"*".key"; then | |
log "ERROR" "Failed to sign zone for $domain" | |
exit 1 | |
fi | |
# Generate DS record | |
if ! ldns-key2ds -n -2 "/var/nsd/zones/master/$domain.zone.signed" \ | |
> "/var/nsd/zones/master/$domain.ds"; then | |
log "WARN" "Failed to generate DS record for $domain" | |
else | |
log "INFO" "DS record for $domain saved to /var/nsd/zones/master/$domain.ds" | |
fi | |
done | |
# Set permissions | |
chown -R _nsd:_nsd /var/nsd/zones/master | |
# Start NSD | |
rcctl enable nsd | |
rcctl start nsd | |
# Wait up to 10 seconds for NSD to start | |
local timeout=10 | |
local counter=0 | |
while ! rcctl check nsd | grep -q "nsd(ok)" && (( counter < timeout )); do | |
sleep 1 | |
((counter++)) | |
done | |
# Verify NSD is running | |
if ! rcctl check nsd | grep -q "nsd(ok)"; then | |
log "ERROR" "NSD failed to start" | |
log "INFO" "Check logs with: tail -f /var/log/daemon" | |
exit 1 | |
fi | |
log "INFO" "NSD setup completed" | |
} | |
# Verify NSD configuration | |
verify_nsd() { | |
log "INFO" "Verifying NSD configuration" | |
local validation_failures=0 | |
for domain_entry in "${ALL_DOMAINS[@]}"; do | |
local domain="${domain_entry%%:*}" | |
# Test A record | |
log "INFO" "Testing A record for $domain" | |
local a_record=$(dig @"$MAIN_IP" "$domain" A +short) | |
if [[ "$a_record" != "$MAIN_IP" ]]; then | |
log "ERROR" "NSD not responding correctly for $domain A record" | |
((validation_failures++)) | |
fi | |
# Test DNSSEC | |
log "INFO" "Testing DNSSEC for $domain" | |
local dnskey=$(dig @"$MAIN_IP" "$domain" DNSKEY +short) | |
if [[ -z "$dnskey" ]]; then | |
log "ERROR" "DNSSEC not properly configured for $domain" | |
((validation_failures++)) | |
else | |
log "INFO" "DNSSEC configured properly for $domain" | |
fi | |
done | |
if (( validation_failures > 0 )); then | |
log "WARN" "NSD verification found $validation_failures issues" | |
else | |
log "INFO" "NSD verification successful" | |
fi | |
} | |
# Configure HTTP server for ACME challenges | |
setup_http() { | |
log "INFO" "Setting up HTTP server for ACME challenges" | |
cat > "/etc/httpd.conf" <<EOF | |
# HTTP server for ACME challenges | |
server "acme" { | |
listen on $MAIN_IP port 80 | |
location "/.well-known/acme-challenge/*" { | |
root "/acme" | |
request strip 2 | |
} | |
location "*" { | |
block return 301 "https://\$HTTP_HOST\$REQUEST_URI" | |
} | |
} | |
EOF | |
# Verify configuration | |
if ! httpd -n; then | |
log "ERROR" "Invalid httpd configuration" | |
exit 1 | |
fi | |
rcctl enable httpd | |
rcctl start httpd | |
# Wait up to 10 seconds for httpd to start | |
local timeout=10 | |
local counter=0 | |
while ! rcctl check httpd | grep -q "httpd(ok)" && (( counter < timeout )); do | |
sleep 1 | |
((counter++)) | |
done | |
if ! rcctl check httpd | grep -q "httpd(ok)"; then | |
log "ERROR" "HTTP server failed to start" | |
log "INFO" "Check logs with: tail -f /var/log/daemon" | |
exit 1 | |
fi | |
# Test ACME challenge path | |
echo "test_acme" > "/var/www/acme/.well-known/acme-challenge/test_acme" | |
local http_status=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost/.well-known/acme-challenge/test_acme") | |
rm "/var/www/acme/.well-known/acme-challenge/test_acme" | |
if [[ "$http_status" != "200" ]]; then | |
log "ERROR" "ACME challenge path is not accessible (HTTP $http_status)" | |
exit 1 | |
fi | |
log "INFO" "HTTP server setup completed" | |
} | |
# Configure and run ACME client for certificates | |
setup_acme() { | |
log "INFO" "Setting up ACME client for Let's Encrypt certificates" | |
# Generate private key if needed | |
if [[ ! -f "/etc/acme/letsencrypt_privkey.pem" ]]; then | |
log "INFO" "Generating ACME account private key" | |
openssl genpkey -algorithm RSA -out "/etc/acme/letsencrypt_privkey.pem" -pkeyopt rsa_keygen_bits:4096 | |
chmod 600 "/etc/acme/letsencrypt_privkey.pem" | |
fi | |
# Create ACME client configuration | |
cat > "/etc/acme-client.conf" <<EOF | |
# ACME client configuration | |
authority letsencrypt { | |
api url "https://acme-v02.api.letsencrypt.org/directory" | |
account key "/etc/acme/letsencrypt_privkey.pem" | |
} | |
EOF | |
# Add domain configurations | |
for domain_entry in "${ALL_DOMAINS[@]}"; do | |
local domain="${domain_entry%%:*}" | |
local subdomains="${domain_entry#*:}" | |
local alt_names="" | |
if [[ -n "$subdomains" && "$subdomains" != "$domain" ]]; then | |
local subdomain_list="" | |
for subdomain in ${(s:,:)subdomains}; do | |
[[ -n "$subdomain_list" ]] && subdomain_list+=", " | |
subdomain_list+="$subdomain.$domain" | |
done | |
alt_names="alternative names { $subdomain_list }" | |
fi | |
cat >> "/etc/acme-client.conf" <<EOF | |
domain "$domain" { | |
$alt_names | |
domain key "/etc/ssl/private/$domain.key" | |
domain full chain certificate "/etc/ssl/$domain.fullchain.pem" | |
sign with letsencrypt | |
challengedir "/var/www/acme" | |
} | |
EOF | |
done | |
# Validate configuration | |
if ! acme-client -n; then | |
log "ERROR" "Invalid ACME client configuration" | |
exit 1 | |
fi | |
# Issue certificates | |
for domain_entry in "${ALL_DOMAINS[@]}"; do | |
local domain="${domain_entry%%:*}" | |
log "INFO" "Issuing certificate for $domain" | |
# Verify DNS resolution | |
local dns_check=$(dig @"$MAIN_IP" "$domain" A +short) | |
if [[ "$dns_check" != "$MAIN_IP" ]]; then | |
log "WARN" "DNS for $domain doesn't resolve to $MAIN_IP, skipping certificate" | |
FAILED_CERTS[$domain]=1 | |
continue | |
fi | |
# Test ACME challenge | |
echo "test_$domain" > "/var/www/acme/.well-known/acme-challenge/test_$domain" | |
local http_status=$(curl -s -o /dev/null -w "%{http_code}" "http://$domain/.well-known/acme-challenge/test_$domain") | |
rm "/var/www/acme/.well-known/acme-challenge/test_$domain" | |
if [[ "$http_status" != "200" ]]; then | |
log "WARN" "HTTP challenge test failed for $domain, skipping certificate" | |
FAILED_CERTS[$domain]=1 | |
continue | |
fi | |
# Issue certificate | |
if acme-client -v "$domain"; then | |
log "INFO" "Certificate issued successfully for $domain" | |
# Generate TLSA record with proper zone serial update | |
if [[ -f "/etc/ssl/$domain.fullchain.pem" ]]; then | |
local tlsa_record=$(openssl x509 -noout -pubkey -in "/etc/ssl/$domain.fullchain.pem" | | |
openssl pkey -pubin -outform der | openssl dgst -sha256 | awk '{print $2}') | |
# Update zone serial before modifying | |
update_zone_serial "/var/nsd/zones/master/$domain.zone" | |
# Add TLSA record | |
echo "_443._tcp.$domain. IN TLSA 3 1 1 $tlsa_record" >> "/var/nsd/zones/master/$domain.zone" | |
# Re-sign zone | |
local entropy=$(head -c 16 /dev/random | openssl sha1 | awk '{print $2}') | |
if ! ldns-signzone -n -p -s "$entropy" \ | |
"/var/nsd/zones/master/$domain.zone" \ | |
"/var/nsd/zones/master/K$domain+"*".key"; then | |
log "ERROR" "Failed to re-sign zone for $domain after adding TLSA record" | |
else | |
log "INFO" "Zone re-signed for $domain after adding TLSA record" | |
fi | |
fi | |
else | |
log "WARN" "Failed to issue certificate for $domain" | |
FAILED_CERTS[$domain]=1 | |
fi | |
done | |
# Reload NSD | |
rcctl reload nsd | |
# Retry failed certificates | |
if (( ${#FAILED_CERTS[@]} > 0 )); then | |
log "INFO" "Retrying ${#FAILED_CERTS[@]} failed certificates" | |
for domain in ${(k)FAILED_CERTS}; do | |
log "INFO" "Retrying certificate for $domain" | |
if acme-client -v "$domain"; then | |
log "INFO" "Certificate issued successfully for $domain on retry" | |
unset FAILED_CERTS[$domain] | |
else | |
log "WARN" "Failed to issue certificate for $domain on retry" | |
fi | |
done | |
fi | |
# Schedule certificate renewal | |
local crontab_tmp="/tmp/crontab_tmp" | |
crontab -l > "$crontab_tmp" 2>/dev/null || echo "" > "$crontab_tmp" | |
# Check if renewal is already scheduled | |
if ! grep -q "acme-client" "$crontab_tmp"; then | |
echo "0 2 * * 1 for domain in ${ALL_DOMAINS[*]%%:*}; do acme-client -v \$domain && rcctl reload relayd; done" >> "$crontab_tmp" | |
crontab "$crontab_tmp" | |
log "INFO" "Scheduled weekly certificate renewal" | |
else | |
log "INFO" "Certificate renewal already scheduled" | |
fi | |
rm "$crontab_tmp" | |
# Report on certificate status | |
local cert_count=$((${#ALL_DOMAINS[@]} - ${#FAILED_CERTS[@]})) | |
log "INFO" "ACME client setup completed: $cert_count certificates issued, ${#FAILED_CERTS[@]} failed" | |
if (( ${#FAILED_CERTS[@]} > 0 )); then | |
log "WARN" "Failed certificates: ${(k)FAILED_CERTS}" | |
fi | |
} | |
# Configure the PF firewall | |
setup_pf() { | |
log "INFO" "Setting up PF firewall" | |
cat > "/etc/pf.conf" <<EOF | |
# PF firewall configuration | |
ext_if = "vio0" | |
# Skip loopback | |
set skip on lo | |
# Block policy | |
set block-policy return | |
# Tables | |
table <bruteforce> persist | |
# Default deny | |
block in log | |
pass out quick | |
# Allow SSH with brute-force protection | |
pass in on \$ext_if inet proto tcp to \$ext_if port 22 keep state \\ | |
(max-src-conn 15, max-src-conn-rate 5/3, overload <bruteforce> flush global) | |
# Allow DNS | |
pass in on \$ext_if inet proto { tcp, udp } to \$ext_if port 53 keep state | |
# Allow HTTP/HTTPS | |
pass in on \$ext_if inet proto tcp to \$ext_if port { 80, 443 } keep state | |
# Allow SMTP | |
pass in on \$ext_if inet proto tcp to \$ext_if port 25 keep state | |
# Relayd anchor | |
anchor "relayd/*" | |
EOF | |
# Verify configuration | |
if ! pfctl -nf "/etc/pf.conf"; then | |
log "ERROR" "Invalid PF configuration" | |
exit 1 | |
fi | |
pfctl -f "/etc/pf.conf" | |
log "INFO" "PF firewall configured" | |
} | |
# Set up PostgreSQL database | |
setup_postgresql() { | |
log "INFO" "Setting up PostgreSQL" | |
if [[ ! -d "/var/postgresql/data/base" ]]; then | |
log "INFO" "Initializing PostgreSQL database" | |
mkdir -p "/var/postgresql/data" | |
chown _postgresql:_postgresql "/var/postgresql/data" | |
su -l _postgresql -c "initdb -D /var/postgresql/data -U postgres -A scram-sha-256 -E UTF8" | |
else | |
log "INFO" "PostgreSQL database already initialized" | |
fi | |
rcctl enable postgresql | |
rcctl start postgresql | |
# Wait up to 20 seconds for PostgreSQL to start | |
local timeout=20 | |
local counter=0 | |
while ! rcctl check postgresql | grep -q "postgresql(ok)" && (( counter < timeout )); do | |
sleep 1 | |
((counter++)) | |
done | |
if ! rcctl check postgresql | grep -q "postgresql(ok)"; then | |
log "ERROR" "PostgreSQL failed to start" | |
log "INFO" "Check logs with: tail -f /var/log/postgresql" | |
exit 1 | |
fi | |
# Create databases for applications | |
for app_entry in "${ALL_APPS[@]}"; do | |
local app="${app_entry%%:*}" | |
local db_name="${app}_production" | |
log "INFO" "Creating database for $app" | |
if ! su -l _postgresql -c "psql -U postgres -tAc \"SELECT 1 FROM pg_database WHERE datname='$db_name'\"" | grep -q 1; then | |
# Create database and user | |
if ! su -l _postgresql -c "createdb -U postgres $db_name"; then | |
log "ERROR" "Failed to create database $db_name" | |
continue | |
fi | |
local password=$(openssl rand -base64 16) | |
if ! su -l _postgresql -c "psql -U postgres -c \"CREATE ROLE $app WITH LOGIN PASSWORD '$password' CREATEDB\""; then | |
log "ERROR" "Failed to create role $app" | |
continue | |
fi | |
if ! su -l _postgresql -c "psql -U postgres -c \"GRANT ALL PRIVILEGES ON DATABASE $db_name TO $app\""; then | |
log "ERROR" "Failed to grant privileges on $db_name to $app" | |
continue | |
fi | |
# Store database credentials | |
mkdir -p "/home/$app/$app/config" 2>/dev/null || true | |
echo "DATABASE_URL=postgres://$app:$password@localhost/$db_name" > "/home/$app/$app/config/.db_password" | |
chmod 600 "/home/$app/$app/config/.db_password" | |
chown "$app:$app" "/home/$app/$app/config/.db_password" | |
log "INFO" "Database $db_name created for $app" | |
else | |
log "INFO" "Database $db_name already exists" | |
fi | |
done | |
log "INFO" "PostgreSQL setup completed" | |
} | |
# Set up Redis for caching | |
setup_redis() { | |
log "INFO" "Setting up Redis" | |
mkdir -p "/var/redis" | |
chown _redis:_redis "/var/redis" | |
cat > "/etc/redis.conf" <<EOF | |
# Redis configuration | |
bind 127.0.0.1 | |
port 6379 | |
dir /var/redis/ | |
maxmemory 256mb | |
maxmemory-policy allkeys-lru | |
EOF | |
rcctl enable redis | |
rcctl start redis | |
# Wait up to 10 seconds for Redis to start | |
local timeout=10 | |
local counter=0 | |
while ! rcctl check redis | grep -q "redis(ok)" && (( counter < timeout )); do | |
sleep 1 | |
((counter++)) | |
done | |
if ! rcctl check redis | grep -q "redis(ok)"; then | |
log "ERROR" "Redis failed to start" | |
log "INFO" "Check logs with: tail -f /var/log/daemon" | |
exit 1 | |
fi | |
log "INFO" "Redis setup completed" | |
} | |
# Deploy Rails applications | |
deploy_rails_apps() { | |
log "INFO" "Deploying Rails applications" | |
for app_entry in "${ALL_APPS[@]}"; do | |
local app="${app_entry%%:*}" | |
local domain="${app_entry#*:}" | |
local app_port=$(generate_random_port) | |
APP_PORTS[$app]=$app_port | |
log "INFO" "Setting up $app on port $app_port" | |
# Create user if needed | |
if ! id "$app" >/dev/null 2>&1; then | |
useradd -m -s /bin/ksh "$app" | |
log "INFO" "Created user $app" | |
fi | |
local app_dir="/home/$app/$app" | |
# Verify application | |
if [[ ! -d "$app_dir" ]]; then | |
log "WARN" "Application directory $app_dir doesn't exist, skipping" | |
continue | |
fi | |
if [[ ! -f "$app_dir/Gemfile" ]]; then | |
log "WARN" "Gemfile not found in $app_dir, skipping" | |
continue | |
fi | |
# Setup application | |
chown -R "$app:$app" "/home/$app" | |
# Install required gems | |
if ! su -l "$app" -c "gem install --user-install bundler falcon"; then | |
log "ERROR" "Failed to install bundler and falcon for $app" | |
continue | |
fi | |
# Install application dependencies | |
if ! su -l "$app" -c "cd $app_dir && bundle config set --local without 'development test' && bundle install"; then | |
log "ERROR" "Failed to run bundle install for $app" | |
continue | |
fi | |
# Create startup script | |
cat > "/etc/rc.d/$app" <<EOF | |
#!/bin/ksh | |
# Rails application service for $app | |
daemon="/bin/ksh -c 'cd $app_dir && export RAILS_ENV=production && \\\$HOME/.gem/ruby/*/bin/bundle exec \\\$HOME/.gem/ruby/*/bin/falcon serve -b tcp://127.0.0.1:$app_port'" | |
daemon_user="$app" | |
. /etc/rc.d/rc.subr | |
rc_pre() { | |
pledge stdio rpath wpath cpath inet dns proc exec fattr | |
} | |
rc_cmd \$1 | |
EOF | |
chmod +x "/etc/rc.d/$app" | |
rcctl enable "$app" | |
rcctl start "$app" | |
# Wait up to 30 seconds for the app to start | |
local timeout=30 | |
local counter=0 | |
while ! rcctl check "$app" | grep -q "$app(ok)" && (( counter < timeout )); do | |
sleep 1 | |
((counter++)) | |
done | |
if ! rcctl check "$app" | grep -q "$app(ok)"; then | |
log "ERROR" "$app failed to start" | |
log "INFO" "Check logs with: tail -f /var/log/daemon" | |
else | |
log "INFO" "$app started successfully on port $app_port" | |
fi | |
done | |
log "INFO" "Rails applications deployed" | |
} | |
# Configure relayd for reverse proxy | |
setup_relayd() { | |
log "INFO" "Setting up relayd" | |
cat > "/etc/relayd.conf" <<EOF | |
# Relayd configuration | |
ext_ip="$MAIN_IP" | |
table <acme_client> { 127.0.0.1 } | |
http protocol "secure_rails" { | |
# Forward client information | |
match request header set "X-Forwarded-For" value "\$REMOTE_ADDR" | |
match request header set "X-Forwarded-Proto" value "https" | |
# Security headers | |
match response header set "Strict-Transport-Security" value "max-age=31536000; includeSubDomains; preload" | |
match response header set "Content-Security-Policy" value "default-src https: 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; object-src 'none';" | |
match response header set "X-Content-Type-Options" value "nosniff" | |
match response header set "X-Frame-Options" value "SAMEORIGIN" | |
match response header set "Referrer-Policy" value "strict-origin" | |
match response header set "X-XSS-Protection" value "1; mode=block" | |
# Enable websockets for Action Cable | |
http websockets | |
} | |
# HTTP relay for ACME challenges | |
relay "http_relay" { | |
listen on \$ext_ip port 80 | |
forward to <acme_client> port 80 | |
} | |
EOF | |
# Add application relays | |
for app_entry in "${ALL_APPS[@]}"; do | |
local app="${app_entry%%:*}" | |
local domain="${app_entry#*:}" | |
local app_port="${APP_PORTS[$app]}" | |
if [[ -z "$app_port" ]]; then | |
log "WARN" "No port assigned for $app, skipping relay configuration" | |
continue | |
fi | |
cat >> "/etc/relayd.conf" <<EOF | |
# $domain relay for $app | |
table <${app}_backend> { 127.0.0.1 } | |
relay "${app}_relay" { | |
listen on \$ext_ip port 443 tls | |
protocol "secure_rails" | |
forward to <${app}_backend> port $app_port | |
tls keypair "$domain" | |
} | |
EOF | |
done | |
# Verify configuration | |
if ! relayd -n; then | |
log "ERROR" "Invalid relayd configuration" | |
exit 1 | |
fi | |
rcctl enable relayd | |
rcctl start relayd | |
# Wait up to 10 seconds for relayd to start | |
local timeout=10 | |
local counter=0 | |
while ! rcctl check relayd | grep -q "relayd(ok)" && (( counter < timeout )); do | |
sleep 1 | |
((counter++)) | |
done | |
if ! rcctl check relayd | grep -q "relayd(ok)"; then | |
log "ERROR" "relayd failed to start" | |
log "INFO" "Check logs with: tail -f /var/log/daemon" | |
exit 1 | |
fi | |
log "INFO" "relayd setup completed" | |
} | |
# Set up OpenSMTPD for email | |
setup_email() { | |
log "INFO" "Setting up OpenSMTPD for email" | |
# Create mail storage directories | |
mkdir -p /var/vmail | |
chown -R _smtpd:_smtpd /var/vmail | |
# Configure OpenSMTPD | |
cat > /etc/mail/smtpd.conf <<EOF | |
# OpenSMTPD configuration | |
table aliases file:/etc/mail/aliases | |
table domains { "brgen.no", "pub.attorney" } | |
# Listen for incoming mail | |
listen on 0.0.0.0 port 25 | |
# Local mail delivery | |
action "local" maildir "/var/vmail/%{dest.domain}/%{dest.user}" | |
# Outbound relay | |
action "relay" relay | |
# Accept mail for our domains | |
match from any for domain <domains> action "local" | |
# Allow outbound mail from local users | |
match from local for any action "relay" | |
EOF | |
# Setup aliases | |
cat > /etc/mail/aliases <<EOF | |
# Mail aliases | |
root: postmaster | |
EOF | |
makemap /etc/mail/aliases | |
# Start OpenSMTPD | |
rcctl enable smtpd | |
rcctl start smtpd | |
# Wait up to 10 seconds for OpenSMTPD to start | |
local timeout=10 | |
local counter=0 | |
while ! rcctl check smtpd | grep -q "smtpd(ok)" && (( counter < timeout )); do | |
sleep 1 | |
((counter++)) | |
done | |
if ! rcctl check smtpd | grep -q "smtpd(ok)"; then | |
log "ERROR" "OpenSMTPD failed to start" | |
log "INFO" "Check logs with: tail -f /var/log/maillog" | |
exit 1 | |
fi | |
log "INFO" "OpenSMTPD setup completed" | |
} | |
# Run a health check on all services | |
run_health_check() { | |
log "INFO" "Running health check on all services" | |
local services=("nsd" "httpd" "postgresql" "redis" "relayd" "smtpd") | |
local rails_services=() | |
# Add Rails services | |
for app in "${ALL_APPS[@]%%:*}"; do | |
rails_services+=("$app") | |
done | |
# Check system services | |
log "INFO" "Checking system services" | |
for service in "${services[@]}"; do | |
if rcctl check "$service" | grep -q "$service(ok)"; then | |
log "INFO" "$service: OK" | |
else | |
log "ERROR" "$service: FAILED" | |
fi | |
done | |
# Check Rails services | |
log "INFO" "Checking Rails services" | |
for service in "${rails_services[@]}"; do | |
if rcctl check "$service" | grep -q "$service(ok)"; then | |
log "INFO" "$service: OK" | |
else | |
log "ERROR" "$service: FAILED" | |
fi | |
done | |
# Check DNS and certificates | |
log "INFO" "Checking DNS and certificates" | |
for domain_entry in "${ALL_DOMAINS[@]}"; do | |
local domain="${domain_entry%%:*}" | |
# Check DNS | |
local dns_check=$(dig @"$MAIN_IP" "$domain" A +short) | |
if [[ "$dns_check" == "$MAIN_IP" ]]; then | |
log "INFO" "DNS for $domain: OK" | |
else | |
log "ERROR" "DNS for $domain: FAILED ($dns_check)" | |
fi | |
# Check certificate | |
if [[ -f "/etc/ssl/$domain.fullchain.pem" ]]; then | |
local cert_expiry=$(openssl x509 -noout -enddate -in "/etc/ssl/$domain.fullchain.pem" | cut -d= -f2) | |
log "INFO" "Certificate for $domain: OK (expires $cert_expiry)" | |
else | |
log "ERROR" "Certificate for $domain: MISSING" | |
fi | |
done | |
log "INFO" "Health check completed" | |
} | |
# Main function with state management | |
main() { | |
# Initialize logging | |
init_log | |
log "INFO" "Starting OpenBSD setup script (v$SCRIPT_VERSION)" | |
# Process command line arguments | |
if [[ "$@" == *"--help"* ]]; then | |
show_help | |
fi | |
if [[ "$@" == *"--check"* ]]; then | |
log "INFO" "Running health check" | |
run_health_check | |
exit 0 | |
fi | |
# Stage management | |
local current_state="stage_1" | |
if [[ -f "$STATE_FILE" ]]; then | |
current_state=$(cat "$STATE_FILE") | |
log "INFO" "Found saved state: $current_state" | |
fi | |
# Resume from saved state if requested | |
if [[ "$@" == *"--resume"* ]] && [[ -f "$STATE_FILE" ]]; then | |
log "INFO" "Resuming from state: $current_state" | |
else | |
# Start from beginning unless resume specified | |
if [[ "$@" != *"--resume"* ]]; then | |
current_state="stage_1" | |
save_state "$current_state" | |
fi | |
fi | |
# Execute stages based on current state | |
case "$current_state" in | |
"stage_1") | |
log "INFO" "Starting Stage 1: DNS and certificates" | |
check_prerequisites | |
install_packages | |
cleanup_nsd | |
setup_nsd | |
verify_nsd | |
setup_http | |
setup_pf | |
setup_acme | |
log "INFO" "Stage 1 completed" | |
log "INFO" "Please verify DNS propagation and correct setup before proceeding to Stage 2" | |
log "INFO" "To check DNS: dig @8.8.8.8 yourdomain.com" | |
log "INFO" "To check certificates: ls -l /etc/ssl/*.fullchain.pem" | |
log "INFO" "When ready, run: doas zsh openbsd.sh --resume" | |
save_state "stage_2" | |
;; | |
"stage_2") | |
log "INFO" "Starting Stage 2: Services and applications" | |
setup_postgresql | |
setup_redis | |
deploy_rails_apps | |
setup_relayd | |
setup_email | |
run_health_check | |
log "INFO" "Stage 2 completed" | |
log "INFO" "Setup complete! Your OpenBSD server is now configured with:" | |
log "INFO" "- NSD with DNSSEC" | |
log "INFO" "- Let's Encrypt certificates with ACME" | |
log "INFO" "- PostgreSQL and Redis" | |
log "INFO" "- Rails applications deployed with Falcon" | |
log "INFO" "- Relayd reverse proxy with security headers" | |
log "INFO" "- OpenSMTPD for email" | |
save_state "completed" | |
;; | |
"completed") | |
log "INFO" "Setup already completed" | |
log "INFO" "To run a health check: doas zsh openbsd.sh --check" | |
log "INFO" "To restart from scratch: rm $STATE_FILE && doas zsh openbsd.sh" | |
;; | |
*) | |
log "ERROR" "Unknown state: $current_state" | |
exit 1 | |
;; | |
esac | |
log "INFO" "Script execution finished" | |
} | |
# Run main function with all arguments | |
main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment