nobalmako 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/README.md +112 -0
  2. package/components.json +22 -0
  3. package/dist/nobalmako.js +272 -0
  4. package/drizzle/0000_pink_spiral.sql +126 -0
  5. package/drizzle/meta/0000_snapshot.json +1027 -0
  6. package/drizzle/meta/_journal.json +13 -0
  7. package/drizzle.config.ts +10 -0
  8. package/eslint.config.mjs +18 -0
  9. package/next.config.ts +7 -0
  10. package/package.json +80 -0
  11. package/postcss.config.mjs +7 -0
  12. package/public/file.svg +1 -0
  13. package/public/globe.svg +1 -0
  14. package/public/next.svg +1 -0
  15. package/public/vercel.svg +1 -0
  16. package/public/window.svg +1 -0
  17. package/server/index.ts +118 -0
  18. package/src/app/api/api-keys/[id]/route.ts +147 -0
  19. package/src/app/api/api-keys/route.ts +151 -0
  20. package/src/app/api/audit-logs/route.ts +84 -0
  21. package/src/app/api/auth/forgot-password/route.ts +47 -0
  22. package/src/app/api/auth/login/route.ts +99 -0
  23. package/src/app/api/auth/logout/route.ts +15 -0
  24. package/src/app/api/auth/me/route.ts +23 -0
  25. package/src/app/api/auth/mfa/setup/route.ts +33 -0
  26. package/src/app/api/auth/mfa/verify/route.ts +45 -0
  27. package/src/app/api/auth/register/route.ts +140 -0
  28. package/src/app/api/auth/reset-password/route.ts +52 -0
  29. package/src/app/api/auth/update/route.ts +71 -0
  30. package/src/app/api/auth/verify/route.ts +39 -0
  31. package/src/app/api/environments/route.ts +227 -0
  32. package/src/app/api/team-members/route.ts +385 -0
  33. package/src/app/api/teams/route.ts +217 -0
  34. package/src/app/api/variable-history/route.ts +218 -0
  35. package/src/app/api/variables/route.ts +476 -0
  36. package/src/app/api/webhooks/route.ts +77 -0
  37. package/src/app/api-keys/APIKeysClient.tsx +316 -0
  38. package/src/app/api-keys/page.tsx +10 -0
  39. package/src/app/api-reference/page.tsx +324 -0
  40. package/src/app/audit-log/AuditLogClient.tsx +229 -0
  41. package/src/app/audit-log/page.tsx +10 -0
  42. package/src/app/auth/forgot-password/page.tsx +121 -0
  43. package/src/app/auth/login/LoginForm.tsx +145 -0
  44. package/src/app/auth/login/page.tsx +11 -0
  45. package/src/app/auth/register/RegisterForm.tsx +156 -0
  46. package/src/app/auth/register/page.tsx +16 -0
  47. package/src/app/auth/reset-password/page.tsx +160 -0
  48. package/src/app/dashboard/DashboardClient.tsx +219 -0
  49. package/src/app/dashboard/page.tsx +11 -0
  50. package/src/app/docs/page.tsx +251 -0
  51. package/src/app/favicon.ico +0 -0
  52. package/src/app/globals.css +123 -0
  53. package/src/app/layout.tsx +35 -0
  54. package/src/app/page.tsx +231 -0
  55. package/src/app/profile/ProfileClient.tsx +230 -0
  56. package/src/app/profile/page.tsx +10 -0
  57. package/src/app/project/[id]/ProjectDetailsClient.tsx +512 -0
  58. package/src/app/project/[id]/page.tsx +17 -0
  59. package/src/bin/nobalmako.ts +341 -0
  60. package/src/components/ApiKeysManager.tsx +529 -0
  61. package/src/components/AppLayout.tsx +193 -0
  62. package/src/components/BulkActions.tsx +138 -0
  63. package/src/components/CreateEnvironmentDialog.tsx +207 -0
  64. package/src/components/CreateTeamDialog.tsx +174 -0
  65. package/src/components/CreateVariableDialog.tsx +311 -0
  66. package/src/components/DeleteEnvironmentDialog.tsx +104 -0
  67. package/src/components/DeleteTeamDialog.tsx +112 -0
  68. package/src/components/DeleteVariableDialog.tsx +103 -0
  69. package/src/components/EditEnvironmentDialog.tsx +202 -0
  70. package/src/components/EditMemberDialog.tsx +143 -0
  71. package/src/components/EditTeamDialog.tsx +178 -0
  72. package/src/components/EditVariableDialog.tsx +231 -0
  73. package/src/components/ImportVariablesDialog.tsx +347 -0
  74. package/src/components/InviteMemberDialog.tsx +191 -0
  75. package/src/components/LeaveProjectDialog.tsx +111 -0
  76. package/src/components/MFASettings.tsx +136 -0
  77. package/src/components/ProjectDiff.tsx +123 -0
  78. package/src/components/Providers.tsx +24 -0
  79. package/src/components/RemoveMemberDialog.tsx +112 -0
  80. package/src/components/SearchDialog.tsx +276 -0
  81. package/src/components/SecurityOverview.tsx +92 -0
  82. package/src/components/TeamMembersManager.tsx +103 -0
  83. package/src/components/VariableHistoryDialog.tsx +265 -0
  84. package/src/components/WebhooksManager.tsx +169 -0
  85. package/src/components/ui/alert-dialog.tsx +160 -0
  86. package/src/components/ui/alert.tsx +59 -0
  87. package/src/components/ui/avatar.tsx +53 -0
  88. package/src/components/ui/badge.tsx +46 -0
  89. package/src/components/ui/button.tsx +62 -0
  90. package/src/components/ui/card.tsx +92 -0
  91. package/src/components/ui/checkbox.tsx +32 -0
  92. package/src/components/ui/dialog.tsx +143 -0
  93. package/src/components/ui/dropdown-menu.tsx +257 -0
  94. package/src/components/ui/input.tsx +21 -0
  95. package/src/components/ui/label.tsx +24 -0
  96. package/src/components/ui/select.tsx +190 -0
  97. package/src/components/ui/separator.tsx +28 -0
  98. package/src/components/ui/sonner.tsx +37 -0
  99. package/src/components/ui/switch.tsx +31 -0
  100. package/src/components/ui/table.tsx +117 -0
  101. package/src/components/ui/tabs.tsx +66 -0
  102. package/src/components/ui/textarea.tsx +18 -0
  103. package/src/hooks/use-api-keys.ts +95 -0
  104. package/src/hooks/use-audit-logs.ts +58 -0
  105. package/src/hooks/use-auth.tsx +121 -0
  106. package/src/hooks/use-environments.ts +33 -0
  107. package/src/hooks/use-project-permissions.ts +49 -0
  108. package/src/hooks/use-team-members.ts +30 -0
  109. package/src/hooks/use-teams.ts +33 -0
  110. package/src/hooks/use-variables.ts +38 -0
  111. package/src/lib/audit.ts +36 -0
  112. package/src/lib/auth.ts +108 -0
  113. package/src/lib/crypto.ts +39 -0
  114. package/src/lib/db.ts +15 -0
  115. package/src/lib/dynamic-providers.ts +19 -0
  116. package/src/lib/email.ts +110 -0
  117. package/src/lib/mail.ts +51 -0
  118. package/src/lib/permissions.ts +51 -0
  119. package/src/lib/schema.ts +240 -0
  120. package/src/lib/seed.ts +107 -0
  121. package/src/lib/utils.ts +6 -0
  122. package/src/lib/webhooks.ts +42 -0
  123. package/tsconfig.json +34 -0
@@ -0,0 +1,16 @@
1
+ import { Metadata } from "next";
2
+ import RegisterForm from "./RegisterForm";
3
+ import { Suspense } from "react";
4
+
5
+ export const metadata: Metadata = {
6
+ title: "Register - Nobalmako",
7
+ description: "Create your Nobalmako account",
8
+ };
9
+
10
+ export default function RegisterPage() {
11
+ return (
12
+ <Suspense fallback={<div className="min-h-screen flex items-center justify-center bg-background p-4">Loading registration...</div>}>
13
+ <RegisterForm />
14
+ </Suspense>
15
+ );
16
+ }
@@ -0,0 +1,160 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect, Suspense } from "react";
4
+ import { useRouter, useSearchParams } from "next/navigation";
5
+ import Link from "next/link";
6
+ import { Button } from "@/components/ui/button";
7
+ import { Input } from "@/components/ui/input";
8
+ import { Label } from "@/components/ui/label";
9
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
10
+ import { Alert, AlertDescription } from "@/components/ui/alert";
11
+ import { Shield, Loader2, CheckCircle2 } from "lucide-react";
12
+
13
+ function ResetPasswordForm() {
14
+ const [password, setPassword] = useState("");
15
+ const [confirmPassword, setConfirmPassword] = useState("");
16
+ const [isLoading, setIsLoading] = useState(false);
17
+ const [success, setSuccess] = useState(false);
18
+ const [error, setError] = useState("");
19
+
20
+ const router = useRouter();
21
+ const searchParams = useSearchParams();
22
+ const token = searchParams.get('token');
23
+
24
+ useEffect(() => {
25
+ if (!token) {
26
+ setError("Invalid or missing reset token. Please request a new link.");
27
+ }
28
+ }, [token]);
29
+
30
+ const handleSubmit = async (e: React.FormEvent) => {
31
+ e.preventDefault();
32
+ setIsLoading(true);
33
+ setError("");
34
+
35
+ if (password !== confirmPassword) {
36
+ setError("Passwords do not match");
37
+ setIsLoading(false);
38
+ return;
39
+ }
40
+
41
+ if (password.length < 8) {
42
+ setError("Password must be at least 8 characters long");
43
+ setIsLoading(false);
44
+ return;
45
+ }
46
+
47
+ try {
48
+ const response = await fetch("/api/auth/reset-password", {
49
+ method: "POST",
50
+ headers: { "Content-Type": "application/json" },
51
+ body: JSON.stringify({ token, password }),
52
+ });
53
+
54
+ if (response.ok) {
55
+ setSuccess(true);
56
+ } else {
57
+ const data = await response.json();
58
+ setError(data.error || "Failed to reset password");
59
+ }
60
+ } catch (err) {
61
+ setError("An unexpected error occurred");
62
+ } finally {
63
+ setIsLoading(false);
64
+ }
65
+ };
66
+
67
+ return (
68
+ <div className="min-h-screen flex items-center justify-center bg-background p-4">
69
+ <Card className="w-full max-w-md">
70
+ <CardHeader className="text-center">
71
+ <div className="flex justify-center mb-4">
72
+ <Shield className="h-12 w-12 text-primary" />
73
+ </div>
74
+ <CardTitle className="text-2xl">Create New Password</CardTitle>
75
+ <CardDescription>
76
+ {success
77
+ ? "Your password has been successfully reset"
78
+ : "Please enter your new password below."}
79
+ </CardDescription>
80
+ </CardHeader>
81
+ <CardContent>
82
+ {success ? (
83
+ <div className="space-y-6 text-center">
84
+ <div className="flex justify-center">
85
+ <div className="h-20 w-20 bg-green-100 rounded-full flex items-center justify-center">
86
+ <CheckCircle2 className="h-10 w-10 text-green-600" />
87
+ </div>
88
+ </div>
89
+ <p className="text-sm text-muted-foreground">
90
+ You can now use your new password to sign in to your account.
91
+ </p>
92
+ <Button asChild className="w-full">
93
+ <Link href="/auth/login">Go to Sign in</Link>
94
+ </Button>
95
+ </div>
96
+ ) : (
97
+ <form onSubmit={handleSubmit} className="space-y-4">
98
+ {error && (
99
+ <Alert variant="destructive">
100
+ <AlertDescription>{error}</AlertDescription>
101
+ </Alert>
102
+ )}
103
+
104
+ <div className="space-y-2">
105
+ <Label htmlFor="password">New Password</Label>
106
+ <Input
107
+ id="password"
108
+ name="password"
109
+ type="password"
110
+ required
111
+ placeholder="At least 8 characters"
112
+ value={password}
113
+ onChange={(e) => setPassword(e.target.value)}
114
+ disabled={isLoading || !token}
115
+ />
116
+ </div>
117
+
118
+ <div className="space-y-2">
119
+ <Label htmlFor="confirmPassword">Confirm New Password</Label>
120
+ <Input
121
+ id="confirmPassword"
122
+ name="confirmPassword"
123
+ type="password"
124
+ required
125
+ placeholder="Repeat your password"
126
+ value={confirmPassword}
127
+ onChange={(e) => setConfirmPassword(e.target.value)}
128
+ disabled={isLoading || !token}
129
+ />
130
+ </div>
131
+
132
+ <Button type="submit" className="w-full" disabled={isLoading || !token}>
133
+ {isLoading ? (
134
+ <>
135
+ <Loader2 className="mr-2 h-4 w-4 animate-spin" />
136
+ Resetting...
137
+ </>
138
+ ) : (
139
+ "Reset Password"
140
+ )}
141
+ </Button>
142
+ </form>
143
+ )}
144
+ </CardContent>
145
+ </Card>
146
+ </div>
147
+ );
148
+ }
149
+
150
+ export default function ResetPasswordPage() {
151
+ return (
152
+ <Suspense fallback={
153
+ <div className="min-h-screen flex items-center justify-center bg-background">
154
+ <Loader2 className="h-8 w-8 animate-spin text-primary" />
155
+ </div>
156
+ }>
157
+ <ResetPasswordForm />
158
+ </Suspense>
159
+ );
160
+ }
@@ -0,0 +1,219 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useState } from "react";
4
+ import { useRouter } from "next/navigation";
5
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
6
+ import { Button } from "@/components/ui/button";
7
+ import { Badge } from "@/components/ui/badge";
8
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
9
+ import { Plus, Settings, Users, Database, Shield, Key, History, Search } from "lucide-react";
10
+ import { useAuth } from "@/hooks/use-auth";
11
+ import { useTeams } from "@/hooks/use-teams";
12
+ import { useEnvironments } from "@/hooks/use-environments";
13
+ import { useVariables } from "@/hooks/use-variables";
14
+ import CreateTeamDialog from "@/components/CreateTeamDialog";
15
+ import CreateVariableDialog from "@/components/CreateVariableDialog";
16
+ import SearchDialog from "@/components/SearchDialog";
17
+ import ApiKeysManager from "@/components/ApiKeysManager";
18
+ import AppLayout from "@/components/AppLayout";
19
+ import { SecurityOverview } from "@/components/SecurityOverview";
20
+ import { MFASettings } from "@/components/MFASettings";
21
+
22
+ export default function DashboardClient() {
23
+ const { user, isLoading: authLoading } = useAuth();
24
+ const { teams, isLoading: teamsLoading } = useTeams();
25
+ const { environments, isLoading: environmentsLoading } = useEnvironments();
26
+ const { variables, isLoading: variablesLoading } = useVariables();
27
+ const router = useRouter();
28
+
29
+ useEffect(() => {
30
+ if (!authLoading && !user) {
31
+ router.push("/auth/login");
32
+ }
33
+ }, [user, authLoading, router]);
34
+
35
+ if (authLoading || teamsLoading || environmentsLoading || variablesLoading) {
36
+ return (
37
+ <div className="min-h-screen flex items-center justify-center">
38
+ <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
39
+ </div>
40
+ );
41
+ }
42
+
43
+ if (!user) {
44
+ return null; // Will redirect
45
+ }
46
+
47
+ return (
48
+ <AppLayout>
49
+ <div className="space-y-8 max-w-7xl mx-auto">
50
+ {/* Welcome Section */}
51
+ <div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
52
+ <div>
53
+ <h1 className="text-3xl font-bold tracking-tight">Welcome back, {user.name}</h1>
54
+ <p className="text-muted-foreground mt-1">
55
+ Here's an overview of your projects and secrets.
56
+ </p>
57
+ </div>
58
+ <div className="flex items-center gap-3">
59
+ <CreateTeamDialog />
60
+ </div>
61
+ </div>
62
+
63
+ {/* Security Health */}
64
+ <section className="space-y-4">
65
+ <div className="flex items-center gap-2">
66
+ <Shield className="h-5 w-5 text-primary" />
67
+ <h2 className="text-xl font-bold tracking-tight">Security Health</h2>
68
+ </div>
69
+ <SecurityOverview variables={variables || []} teams={teams || []} />
70
+ </section>
71
+
72
+ {/* Stats Grid */}
73
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
74
+ <Card className="bg-card border-border shadow-sm">
75
+ <CardHeader className="pb-2">
76
+ <CardTitle className="text-sm font-medium text-muted-foreground flex items-center">
77
+ <Shield className="h-4 w-4 mr-2" />
78
+ Total Projects
79
+ </CardTitle>
80
+ </CardHeader>
81
+ <CardContent>
82
+ <div className="text-3xl font-bold">{teams?.length || 0}</div>
83
+ </CardContent>
84
+ </Card>
85
+ <Card className="bg-card border-border shadow-sm">
86
+ <CardHeader className="pb-2">
87
+ <CardTitle className="text-sm font-medium text-muted-foreground flex items-center">
88
+ <Database className="h-4 w-4 mr-2" />
89
+ Active Environments
90
+ </CardTitle>
91
+ </CardHeader>
92
+ <CardContent>
93
+ <div className="text-3xl font-bold">{environments?.length || 0}</div>
94
+ </CardContent>
95
+ </Card>
96
+ <Card className="bg-card border-border shadow-sm">
97
+ <CardHeader className="pb-2">
98
+ <CardTitle className="text-sm font-medium text-muted-foreground flex items-center">
99
+ <Key className="h-4 w-4 mr-2" />
100
+ Secrets Guarded
101
+ </CardTitle>
102
+ </CardHeader>
103
+ <CardContent>
104
+ <div className="text-3xl font-bold">{variables?.filter(v => v.isSecret).length || 0}</div>
105
+ </CardContent>
106
+ </Card>
107
+ </div>
108
+
109
+ <Tabs defaultValue="projects" className="space-y-6">
110
+ <TabsList className="bg-card border p-1">
111
+ <TabsTrigger value="projects" className="px-6">Projects</TabsTrigger>
112
+ <TabsTrigger value="api-keys" className="px-6">API Keys</TabsTrigger>
113
+ <TabsTrigger value="security" className="px-6">Security</TabsTrigger>
114
+ <TabsTrigger value="activity" className="px-6">Activity</TabsTrigger>
115
+ </TabsList>
116
+
117
+ <TabsContent value="projects" className="space-y-6 outline-none">
118
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
119
+ {teams?.map((team) => (
120
+ <Card
121
+ key={team.id}
122
+ className="group hover:border-primary/50 transition-all cursor-pointer overflow-hidden shadow-sm hover:shadow-md"
123
+ onClick={() => router.push(`/project/${team.id}`)}
124
+ >
125
+ <div
126
+ className="h-2 w-full transition-all group-hover:h-3"
127
+ style={{ backgroundColor: team.color || '#3b82f6' }}
128
+ />
129
+ <CardHeader>
130
+ <div className="flex items-center justify-between">
131
+ <CardTitle className="group-hover:text-primary transition-colors">{team.name}</CardTitle>
132
+ {team.ownerId === user.id && (
133
+ <Badge variant="secondary" className="font-normal">Owner</Badge>
134
+ )}
135
+ </div>
136
+ <CardDescription className="line-clamp-2 min-h-[2.5rem]">
137
+ {team.description || "No description provided."}
138
+ </CardDescription>
139
+ </CardHeader>
140
+ <CardContent>
141
+ <div className="flex items-center gap-4 text-sm text-muted-foreground">
142
+ <div className="flex items-center">
143
+ <Database className="h-4 w-4 mr-1 text-blue-500" />
144
+ {environments?.filter(e => e.teamId === team.id).length || 0} Envs
145
+ </div>
146
+ <div className="flex items-center">
147
+ <Shield className="h-4 w-4 mr-1 text-green-500" />
148
+ {variables?.filter(v => v.teamId === team.id).length || 0} Vars
149
+ </div>
150
+ </div>
151
+ </CardContent>
152
+ </Card>
153
+ ))}
154
+
155
+ <Card
156
+ className="border-dashed flex flex-col items-center justify-center p-8 hover:bg-muted/50 cursor-pointer transition-colors min-h-[180px]"
157
+ onClick={() => {
158
+ const btn = document.querySelector('[data-create-team-btn]') as HTMLButtonElement;
159
+ if (btn) btn.click();
160
+ }}
161
+ >
162
+ <div className="h-12 w-12 rounded-full bg-muted flex items-center justify-center mb-4">
163
+ <Plus className="h-6 w-6 text-muted-foreground" />
164
+ </div>
165
+ <h3 className="font-semibold">Create Project</h3>
166
+ <p className="text-sm text-muted-foreground text-center mt-1">
167
+ Add a new project to start managing variables.
168
+ </p>
169
+ </Card>
170
+ </div>
171
+ </TabsContent>
172
+
173
+ <TabsContent value="api-keys" className="outline-none">
174
+ <Card className="shadow-sm">
175
+ <CardHeader>
176
+ <CardTitle>API Keys</CardTitle>
177
+ <CardDescription>
178
+ Manage access keys for fetching your environment variables programmatically.
179
+ </CardDescription>
180
+ </CardHeader>
181
+ <CardContent>
182
+ <ApiKeysManager />
183
+ </CardContent>
184
+ </Card>
185
+ </TabsContent>
186
+
187
+ <TabsContent value="security" className="outline-none">
188
+ <MFASettings initialEnabled={user.mfaEnabled || false} />
189
+ </TabsContent>
190
+
191
+ <TabsContent value="activity" className="outline-none">
192
+ <Card className="shadow-sm">
193
+ <CardHeader>
194
+ <CardTitle>Global Activity Log</CardTitle>
195
+ <CardDescription>
196
+ Recent changes across all your projects.
197
+ </CardDescription>
198
+ </CardHeader>
199
+ <CardContent>
200
+ <div className="text-center py-12">
201
+ <div className="inline-flex h-12 w-12 items-center justify-center rounded-full bg-muted mb-4">
202
+ <History className="h-6 w-6 text-muted-foreground" />
203
+ </div>
204
+ <h3 className="font-semibold">Full Activity History</h3>
205
+ <p className="text-sm text-muted-foreground mt-1 mb-6 max-w-xs mx-auto">
206
+ View a detailed audit trail of every change made to your environment variables.
207
+ </p>
208
+ <Button variant="outline" onClick={() => router.push('/audit-log')}>
209
+ View All Audit Logs
210
+ </Button>
211
+ </div>
212
+ </CardContent>
213
+ </Card>
214
+ </TabsContent>
215
+ </Tabs>
216
+ </div>
217
+ </AppLayout>
218
+ );
219
+ }
@@ -0,0 +1,11 @@
1
+ import { Metadata } from "next";
2
+ import DashboardClient from "./DashboardClient";
3
+
4
+ export const metadata: Metadata = {
5
+ title: "Dashboard - Nobalmako",
6
+ description: "Manage your environment variables",
7
+ };
8
+
9
+ export default function DashboardPage() {
10
+ return <DashboardClient />;
11
+ }
@@ -0,0 +1,251 @@
1
+ 'use client';
2
+
3
+ import AppLayout from "@/components/AppLayout";
4
+ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
5
+ import { Badge } from "@/components/ui/badge";
6
+ import { Terminal, Shield, Zap, Globe, Lock, Cpu, Server, Database } from "lucide-react";
7
+
8
+ export default function DocsPage() {
9
+ const steps = [
10
+ {
11
+ title: "1. Create a Project",
12
+ description: "Initialize your workspace. A project represents a specific application or service where you'll manage secrets.",
13
+ icon: <Terminal className="h-5 w-5 text-primary" />,
14
+ color: "blue"
15
+ },
16
+ {
17
+ title: "2. Define Environments",
18
+ description: "Set up Development, Staging, and Production. Each environment isolates its own set of encrypted variables.",
19
+ icon: <Globe className="h-5 w-5 text-primary" />,
20
+ color: "emerald"
21
+ },
22
+ {
23
+ title: "3. Import Secrets",
24
+ description: "Add your keys manually or bulk-import via .env files. Nobalmako automatically encrypts everything at rest.",
25
+ icon: <Lock className="h-5 w-5 text-primary" />,
26
+ color: "amber"
27
+ },
28
+ {
29
+ title: "4. Inject via API & SDK",
30
+ description: "Use our CLI or REST API to securely inject credentials into your CI/CD pipelines and local machines.",
31
+ icon: <Zap className="h-5 w-5 text-primary" />,
32
+ color: "purple"
33
+ }
34
+ ];
35
+
36
+ return (
37
+ <AppLayout>
38
+ <div className="max-w-6xl mx-auto py-12 px-6 sm:px-8">
39
+ <header className="mb-16 border-b pb-10">
40
+ <Badge className="mb-4 bg-primary/10 text-primary border-primary/20 hover:bg-primary/20" variant="outline">
41
+ Documentation v1.0.0
42
+ </Badge>
43
+ <h1 className="text-5xl font-extrabold tracking-tight mb-4 text-foreground ">
44
+ Documentation
45
+ </h1>
46
+ <p className="text-xl text-muted-foreground max-w-2xl leading-relaxed">
47
+ Welcome to Nobalmako. Learn how to centralize, secure, and automate your company's environment variables.
48
+ </p>
49
+ </header>
50
+
51
+ <div className="grid md:grid-cols-2 lg:grid-cols-4 gap-6 mb-20">
52
+ {steps.map((step, i) => (
53
+ <Card key={i} className="border-border/50 bg-card hover:bg-muted/50 transition-all hover:shadow-lg group">
54
+ <CardHeader>
55
+ <div className="mb-4 p-3 bg-primary/5 rounded-xl w-fit group-hover:scale-110 transition-transform">
56
+ {step.icon}
57
+ </div>
58
+ <CardTitle className="text-lg font-bold text-foreground ">{step.title}</CardTitle>
59
+ </CardHeader>
60
+ <CardContent>
61
+ <p className="text-sm text-muted-foreground leading-relaxed">
62
+ {step.description}
63
+ </p>
64
+ </CardContent>
65
+ </Card>
66
+ ))}
67
+ </div>
68
+
69
+ <div className="grid lg:grid-cols-12 gap-16">
70
+ <div className="lg:col-span-8 space-y-20">
71
+ <section className="scroll-mt-24">
72
+ <div className="flex items-center gap-4 mb-6">
73
+ <div className="p-2 bg-primary/10 rounded-lg">
74
+ <Shield className="h-6 w-6 text-primary" />
75
+ </div>
76
+ <h2 className="text-3xl font-bold text-foreground ">Security Architecture</h2>
77
+ </div>
78
+ <div className="prose max-w-none">
79
+ <p className="text-lg text-muted-foreground leading-relaxed italic border-l-4 border-primary/20 pl-6 py-2">
80
+ "Nobalmako uses industry-standard AES-256-GCM encryption. Keys are never stored in plain-text, and unique IVs are used for every single secret."
81
+ </p>
82
+ <div className="grid sm:grid-cols-2 gap-8 mt-10">
83
+ <div className="p-6 bg-muted/40 rounded-2xl border border-border/50">
84
+ <h4 className="font-bold flex items-center gap-2 mb-3 text-foreground ">
85
+ <Server className="h-4 w-4 text-primary" /> At Rest
86
+ </h4>
87
+ <p className="text-sm text-muted-foreground">Encryption performed application-side before hitting the database.</p>
88
+ </div>
89
+ <div className="p-6 bg-muted/40 rounded-2xl border border-border/50">
90
+ <h4 className="font-bold flex items-center gap-2 mb-3 text-foreground ">
91
+ <Cpu className="h-4 w-4 text-primary" /> In Transit
92
+ </h4>
93
+ <p className="text-sm text-muted-foreground">Mandatory TLS 1.3 encryption for all API calls and browser traffic.</p>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ </section>
98
+
99
+ <section className="scroll-mt-24">
100
+ <div className="flex items-center gap-4 mb-6">
101
+ <div className="p-2 bg-primary/10 rounded-lg">
102
+ <Zap className="h-6 w-6 text-primary" />
103
+ </div>
104
+ <h2 className="text-3xl font-bold text-foreground ">Code Integration (SDKs)</h2>
105
+ </div>
106
+ <p className="text-muted-foreground mb-6">
107
+ Nobalmako provides official client libraries for major languages to simplify secret injection.
108
+ </p>
109
+ <div className="grid gap-6">
110
+ <Card className="border-border/50">
111
+ <CardHeader className="pb-2">
112
+ <CardTitle className="text-sm font-semibold uppercase tracking-wider opacity-60">JavaScript / TypeScript</CardTitle>
113
+ </CardHeader>
114
+ <CardContent>
115
+ <pre className="bg-slate-900 text-primary p-4 rounded-xl overflow-x-auto text-[13px] leading-relaxed font-mono">
116
+ {`npm install @nobalmako/sdk
117
+
118
+ import { nobalmako } from '@nobalmako/sdk';
119
+
120
+ // Automatically loads variables into process.env
121
+ await nobalmako.load({
122
+ project: "my-service",
123
+ environment: "production"
124
+ });`}
125
+ </pre>
126
+ </CardContent>
127
+ </Card>
128
+
129
+ <Card className="border-border/50">
130
+ <CardHeader className="pb-2">
131
+ <CardTitle className="text-sm font-semibold uppercase tracking-wider opacity-60">Python</CardTitle>
132
+ </CardHeader>
133
+ <CardContent>
134
+ <pre className="bg-slate-900 text-primary p-4 rounded-xl overflow-x-auto text-[13px] leading-relaxed font-mono">
135
+ {`pip install nobalmako
136
+
137
+ from nobalmako import nm
138
+
139
+ # Loads variables into os.environ
140
+ nm.load(project="my-service", env="production")`}
141
+ </pre>
142
+ </CardContent>
143
+ </Card>
144
+ </div>
145
+ </section>
146
+
147
+ <section className="scroll-mt-24">
148
+ <div className="flex items-center gap-4 mb-6">
149
+ <div className="p-2 bg-primary/10 rounded-lg">
150
+ <Database className="h-6 w-6 text-primary" />
151
+ </div>
152
+ <h2 className="text-3xl font-bold text-foreground ">Environment Isolation</h2>
153
+ </div>
154
+ <div className="prose max-w-none space-y-6">
155
+ <p className="text-lg text-muted-foreground">
156
+ Prevent "Production leaking into Dev" by defining strict scoped environments. Each environment is a cryptographic silo.
157
+ </p>
158
+ <ul className="grid sm:grid-cols-2 gap-x-12 gap-y-4 list-none p-0">
159
+ <li className="flex items-start gap-3">
160
+ <div className="mt-1.5 h-1.5 w-1.5 rounded-full bg-primary shrink-0" />
161
+ <span className="text-sm font-medium">RBAC for specifically sensitive environments.</span>
162
+ </li>
163
+ <li className="flex items-start gap-3">
164
+ <div className="mt-1.5 h-1.5 w-1.5 rounded-full bg-primary shrink-0" />
165
+ <span className="text-sm font-medium">Environment-specific webhook triggers.</span>
166
+ </li>
167
+ <li className="flex items-start gap-3">
168
+ <div className="mt-1.5 h-1.5 w-1.5 rounded-full bg-primary shrink-0" />
169
+ <span className="text-sm font-medium">Automatic .env generation for local dev.</span>
170
+ </li>
171
+ <li className="flex items-start gap-3">
172
+ <div className="mt-1.5 h-1.5 w-1.5 rounded-full bg-primary shrink-0" />
173
+ <span className="text-sm font-medium">Historical versioning of all secrets.</span>
174
+ </li>
175
+ </ul>
176
+ </div>
177
+ </section>
178
+
179
+ <section id="cli-installation" className="scroll-mt-24">
180
+ <div className="flex items-center gap-4 mb-6">
181
+ <div className="p-2 bg-primary/10 rounded-lg">
182
+ <Terminal className="h-6 w-6 text-primary" />
183
+ </div>
184
+ <h2 className="text-3xl font-bold text-foreground ">CLI Usage</h2>
185
+ </div>
186
+ <div className="prose max-w-none space-y-6">
187
+ <p className="text-lg text-muted-foreground">
188
+ The Nobalmako CLI is the fastest way to sync your environment variables with your local machine or CI/CD pipelines.
189
+ </p>
190
+
191
+ <div className="space-y-4">
192
+ <h4 className="text-sm font-bold uppercase tracking-wider text-muted-foreground">Global Installation</h4>
193
+ <p className="text-sm text-muted-foreground">Download and install the CLI from NPM globally to use it anywhere.</p>
194
+ <div className="bg-slate-900 rounded-xl p-6 font-mono text-sm border border-white/10 group relative">
195
+ <code className="text-primary">npm install -g nobalmako</code>
196
+ </div>
197
+ </div>
198
+
199
+ <div className="space-y-4">
200
+ <h4 className="text-sm font-bold uppercase tracking-wider text-muted-foreground">Authentication</h4>
201
+ <p className="text-sm text-muted-foreground">Authenticate your CLI session to interact with your projects.</p>
202
+ <div className="bg-slate-900 rounded-xl p-6 font-mono text-sm border border-white/10">
203
+ <code className="text-primary">nobalmako login</code>
204
+ </div>
205
+ </div>
206
+
207
+ <div className="space-y-4">
208
+ <h4 className="text-sm font-bold uppercase tracking-wider text-muted-foreground">Pulling Secrets</h4>
209
+ <p className="text-sm text-muted-foreground">Download your environment variables into a local .env file.</p>
210
+ <div className="bg-slate-900 rounded-xl p-6 font-mono text-sm border border-white/10">
211
+ <code className="text-primary">nobalmako pull -p "My Project" -e "development"</code>
212
+ </div>
213
+ </div>
214
+
215
+ <div className="space-y-4">
216
+ <h4 className="text-sm font-bold uppercase tracking-wider text-muted-foreground">Usage in CI/CD</h4>
217
+ <p className="text-sm text-muted-foreground">For GitHub Actions or GitLab CI, we recommend using `npx` with an API token.</p>
218
+ <div className="bg-slate-900 rounded-xl p-6 font-mono text-sm border border-white/10">
219
+ <code className="text-primary">npx nobalmako pull -p "Prod" -e "prod" --token $NOBALMAKO_TOKEN</code>
220
+ </div>
221
+ </div>
222
+ </div>
223
+ </section>
224
+ </div>
225
+
226
+ <aside className="lg:col-span-4 space-y-8">
227
+ <div className="p-8 border border-primary/20 bg-card rounded-[2rem] text-foreground shadow-xl relative overflow-hidden group">
228
+ <div className="absolute top-0 right-0 w-32 h-32 bg-primary/5 rounded-full -mr-16 -mt-16 group-hover:scale-150 transition-transform duration-500" />
229
+ <h3 className="text-2xl font-bold mb-4 relative z-10 text-primary">Need closer help?</h3>
230
+ <p className="text-muted-foreground mb-6 text-sm leading-relaxed relative z-10">
231
+ Our team of security engineers is available for white-glove onboarding and architectural reviews.
232
+ </p>
233
+ <button className="w-full bg-primary text-primary-foreground font-bold py-3 rounded-xl hover:bg-primary/90 transition-colors shadow-lg relative z-10">
234
+ Contact Enterprise Support
235
+ </button>
236
+ </div>
237
+
238
+ <div className="p-8 border border-border/50 rounded-[2rem] bg-card hover:bg-muted/30 transition-colors">
239
+ <h4 className="font-bold mb-4 flex items-center gap-2 text-foreground ">
240
+ <Shield className="h-4 w-4 text-primary" /> compliance
241
+ </h4>
242
+ <p className="text-xs text-muted-foreground leading-relaxed">
243
+ Nobalmako is built to help your organization achieve SOC2 Type II and GDPR compliance by providing immutable logs and proof of secret rotation.
244
+ </p>
245
+ </div>
246
+ </aside>
247
+ </div>
248
+ </div>
249
+ </AppLayout>
250
+ );
251
+ }
Binary file