@ai-content-space/loopx 0.1.2 → 0.1.3

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 (67) hide show
  1. package/README.md +343 -56
  2. package/README.zh-CN.md +392 -0
  3. package/package.json +4 -1
  4. package/plugins/loopx/.codex-plugin/plugin.json +1 -1
  5. package/plugins/loopx/scripts/plugin-install.test.mjs +1 -0
  6. package/plugins/loopx/skills/archive/SKILL.md +39 -0
  7. package/plugins/loopx/skills/build/SKILL.md +111 -9
  8. package/plugins/loopx/skills/clarify/SKILL.md +121 -1
  9. package/plugins/loopx/skills/debug/SKILL.md +296 -0
  10. package/plugins/loopx/skills/debug/condition-based-waiting.md +115 -0
  11. package/plugins/loopx/skills/debug/defense-in-depth.md +122 -0
  12. package/plugins/loopx/skills/debug/find-polluter.sh +63 -0
  13. package/plugins/loopx/skills/debug/root-cause-tracing.md +169 -0
  14. package/plugins/loopx/skills/go-style/SKILL.md +71 -0
  15. package/plugins/loopx/skills/kratos/SKILL.md +74 -0
  16. package/plugins/loopx/skills/kratos/references/advanced-features.md +314 -0
  17. package/plugins/loopx/skills/kratos/references/architecture.md +488 -0
  18. package/plugins/loopx/skills/kratos/references/configuration.md +399 -0
  19. package/plugins/loopx/skills/kratos/references/http-customization.md +512 -0
  20. package/plugins/loopx/skills/kratos/references/middleware-logging.md +400 -0
  21. package/plugins/loopx/skills/kratos/references/proto-api-design.md +432 -0
  22. package/plugins/loopx/skills/kratos/references/security-auth.md +411 -0
  23. package/plugins/loopx/skills/kratos/references/troubleshooting.md +385 -0
  24. package/plugins/loopx/skills/plan/SKILL.md +22 -2
  25. package/plugins/loopx/skills/review/SKILL.md +98 -1
  26. package/plugins/loopx/skills/tdd/SKILL.md +371 -0
  27. package/plugins/loopx/skills/tdd/testing-anti-patterns.md +299 -0
  28. package/plugins/loopx/skills/verify/SKILL.md +139 -0
  29. package/scripts/codex-stop-hook.mjs +71 -0
  30. package/scripts/codex-workflow-hook.mjs +153 -0
  31. package/skills/archive/SKILL.md +39 -0
  32. package/skills/build/SKILL.md +111 -9
  33. package/skills/clarify/SKILL.md +121 -1
  34. package/skills/debug/SKILL.md +296 -0
  35. package/skills/debug/condition-based-waiting.md +115 -0
  36. package/skills/debug/defense-in-depth.md +122 -0
  37. package/skills/debug/find-polluter.sh +63 -0
  38. package/skills/debug/root-cause-tracing.md +169 -0
  39. package/skills/go-style/SKILL.md +71 -0
  40. package/skills/kratos/SKILL.md +74 -0
  41. package/skills/kratos/references/advanced-features.md +314 -0
  42. package/skills/kratos/references/architecture.md +488 -0
  43. package/skills/kratos/references/configuration.md +399 -0
  44. package/skills/kratos/references/http-customization.md +512 -0
  45. package/skills/kratos/references/middleware-logging.md +400 -0
  46. package/skills/kratos/references/proto-api-design.md +432 -0
  47. package/skills/kratos/references/security-auth.md +411 -0
  48. package/skills/kratos/references/troubleshooting.md +385 -0
  49. package/skills/plan/SKILL.md +18 -2
  50. package/skills/review/SKILL.md +98 -1
  51. package/skills/tdd/SKILL.md +371 -0
  52. package/skills/tdd/testing-anti-patterns.md +299 -0
  53. package/skills/verify/SKILL.md +139 -0
  54. package/src/build-runtime.mjs +303 -26
  55. package/src/build-stop-gate.mjs +94 -0
  56. package/src/cli.mjs +47 -5
  57. package/src/codex-exec-runtime.mjs +105 -5
  58. package/src/context-manifest.mjs +172 -0
  59. package/src/install-discovery.mjs +352 -5
  60. package/src/next-skill.mjs +57 -5
  61. package/src/plan-runtime.mjs +79 -122
  62. package/src/review-runtime.mjs +378 -0
  63. package/src/runtime-maintenance.mjs +428 -14
  64. package/src/template-governance.mjs +223 -0
  65. package/src/workflow.mjs +1941 -117
  66. package/src/workspace-context.mjs +166 -0
  67. package/src/workspace-memory.mjs +69 -0
@@ -0,0 +1,411 @@
1
+ # Security & Auth
2
+
3
+ Guide for JWT, Casbin, idempotency, and data masking in Kratos.
4
+
5
+ ## When to Use
6
+
7
+ - Extracting JWT claims from context
8
+ - Setting up Casbin authorization
9
+ - Implementing idempotency middleware
10
+ - Data masking for sensitive fields
11
+
12
+ ---
13
+
14
+ ## JWT Context Extraction
15
+
16
+ ### Get Payload from Context
17
+
18
+ ```go
19
+ import (
20
+ "github.com/go-kratos/kratos/v2/middleware/auth/jwt"
21
+ jwtV4 "github.com/golang-jwt/jwt/v4"
22
+ )
23
+
24
+ func getPayloadFromCtx(ctx context.Context, partName string) (string, error) {
25
+ if claims, ok := jwt.FromContext(ctx); ok {
26
+ if m, ok := claims.(jwtV4.MapClaims); ok {
27
+ if v, ok := m[partName].(string); ok {
28
+ return v, nil
29
+ }
30
+ }
31
+ }
32
+ return "", errors.New("invalid Jwt")
33
+ }
34
+
35
+ // Usage
36
+ func (s *UserService) GetUser(ctx context.Context, req *v1.GetUserRequest) (*v1.GetUserResponse, error) {
37
+ userID, err := getPayloadFromCtx(ctx, "user_id")
38
+ if err != nil {
39
+ return nil, err
40
+ }
41
+ // Use userID in business logic
42
+ }
43
+ ```
44
+
45
+ ### JWT Middleware Setup
46
+
47
+ ```go
48
+ import (
49
+ "github.com/go-kratos/kratos/v2/middleware/auth/jwt"
50
+ jwtV4 "github.com/golang-jwt/jwt/v4"
51
+ )
52
+
53
+ var signingKey = []byte("your-secret-key")
54
+
55
+ // jwt.Server requires a Keyfunc for token verification
56
+ srv := http.NewServer(
57
+ http.Address(":8000"),
58
+ http.Middleware(
59
+ jwt.Server(
60
+ func(token *jwtV4.Token) (interface{}, error) {
61
+ // Verify signing method
62
+ if _, ok := token.Method.(*jwtV4.SigningMethodHMAC); !ok {
63
+ return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
64
+ }
65
+ return signingKey, nil
66
+ },
67
+ jwt.WithSigningMethod(jwtV4.SigningMethodHS256),
68
+ ),
69
+ ),
70
+ )
71
+ ```
72
+
73
+ **Why Keyfunc:** The `jwt.Server` middleware requires a function to retrieve the verification key. This allows for key rotation and multi-key scenarios. For simple cases, return the static key directly.
74
+
75
+ ### JWT Best Practices
76
+
77
+ From industry standards:
78
+
79
+ 1. **Always use HTTPS** - Prevents token interception
80
+ 2. **Limit token refresh** - e.g., 50 times per day max
81
+ 3. **Short token lifetime** - e.g., 15 minutes for access token
82
+ 4. **Use httponly cookies** - Prevent JavaScript access
83
+ 5. **SameSite=Strict** - Prevent CSRF
84
+
85
+ ```go
86
+ // Cookie settings
87
+ http.SetCookie(ctx, &http.Cookie{
88
+ Name: "token",
89
+ Value: token,
90
+ HttpOnly: true,
91
+ SameSite: http.SameSiteStrictMode,
92
+ Secure: true, // HTTPS only
93
+ MaxAge: 900, // 15 minutes
94
+ })
95
+ ```
96
+
97
+ ---
98
+
99
+ ## Casbin Integration
100
+
101
+ ### Model Configuration
102
+
103
+ Use `keyMatch3` for Kratos URL patterns:
104
+
105
+ ```c
106
+ # model.conf
107
+ [request_definition]
108
+ r = sub, dom, obj, act
109
+
110
+ [policy_definition]
111
+ p = sub, dom, obj, act
112
+
113
+ [role_definition]
114
+ g = _, _, _
115
+
116
+ [policy_effect]
117
+ e = some(where (p.eft == allow))
118
+
119
+ [matchers]
120
+ m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && (regexMatch(r.obj, p.obj) || keyMatch3(r.obj, p.obj)) && r.act == p.act
121
+ ```
122
+
123
+ **Why keyMatch3:** Kratos generates URLs like `/api/v1/user/{user_id}`. keyMatch3 matches `{variable}` patterns.
124
+
125
+ ### Middleware Setup
126
+
127
+ ```go
128
+ import (
129
+ "github.com/casbin/casbin/v2"
130
+ casbinM "github.com/go-kratos/kratos/v2/middleware/auth/casbin"
131
+ )
132
+
133
+ func NewCasbinEnforcer() *casbin.Enforcer {
134
+ m := model.NewModelFromFile("model.conf")
135
+ a := fileadapter.NewAdapter("policy.csv")
136
+ e, _ := casbin.NewEnforcer(m, a)
137
+ return e
138
+ }
139
+
140
+ srv := http.NewServer(
141
+ http.Address(":8000"),
142
+ http.Middleware(
143
+ casbinM.Server(
144
+ casbinM.WithEnforcer(NewCasbinEnforcer()),
145
+ casbinM.WithModel(model),
146
+ ),
147
+ ),
148
+ )
149
+ ```
150
+
151
+ ### Get URL in Middleware
152
+
153
+ ```go
154
+ func MyMiddleware() middleware.Middleware {
155
+ return func(handler middleware.Handler) middleware.Handler {
156
+ return func(ctx context.Context, req interface{}) (interface{}, error) {
157
+ if tr, ok := transport.FromServerContext(ctx); ok {
158
+ if hr, ok := tr.(*http.Transport); ok {
159
+ method := hr.Request().Method
160
+ path := hr.Request().RequestURI
161
+
162
+ // Use for Casbin, logging, etc.
163
+ }
164
+ }
165
+ return handler(ctx, req)
166
+ }
167
+ }
168
+ }
169
+ ```
170
+
171
+ ### Policy Watcher (Hot Refresh)
172
+
173
+ ```go
174
+ import "github.com/casbin/casbin/v2/persist"
175
+
176
+ type Watcher struct {
177
+ callback func(string)
178
+ notify chan struct{}
179
+ done chan struct{} // For graceful shutdown
180
+ }
181
+
182
+ func (w *Watcher) SetUpdateCallback(fn func(string)) error {
183
+ w.callback = fn
184
+ go func() {
185
+ for {
186
+ select {
187
+ case <-w.notify:
188
+ fn("policy updated")
189
+ case <-w.done:
190
+ return // Graceful shutdown
191
+ }
192
+ }
193
+ }()
194
+ return nil
195
+ }
196
+
197
+ func (w *Watcher) Close() {
198
+ close(w.done)
199
+ }
200
+
201
+ func (w *Watcher) Update() error {
202
+ w.notify <- struct{}{}
203
+ return nil
204
+ }
205
+
206
+ // Use in biz layer to trigger refresh
207
+ func (uc *RoleUseCase) UpdatePolicy(roles []*Role) error {
208
+ defer uc.watcher.Update() // Notify Casbin to refresh
209
+ // Update policy in database...
210
+ }
211
+ ```
212
+
213
+ ---
214
+
215
+ ## Idempotency Middleware
216
+
217
+ Prevent duplicate operations on non-idempotent endpoints:
218
+
219
+ ### Token-Based Idempotency
220
+
221
+ ```go
222
+ func IdempotentMiddleware() middleware.Middleware {
223
+ return func(handler middleware.Handler) middleware.Handler {
224
+ return func(ctx context.Context, req interface{}) (interface{}, error) {
225
+ tr, ok := transport.FromServerContext(ctx)
226
+ if !ok {
227
+ return nil, errors.New("no transport")
228
+ }
229
+
230
+ // Check whitelist (some operations don't need idempotency)
231
+ if isWhitelisted(tr.Operation()) {
232
+ return handler(ctx, req)
233
+ }
234
+
235
+ // Get idempotency token
236
+ token := tr.RequestHeader().Get("x-idempotent")
237
+ if token == "" {
238
+ return nil, errors.New(400, "MISSING_TOKEN", "x-idempotent header required")
239
+ }
240
+
241
+ // Check if token was used
242
+ if wasUsed(token) {
243
+ return nil, errors.New(409, "TOKEN_USED", "duplicate request")
244
+ }
245
+
246
+ // Mark token as used
247
+ markUsed(token)
248
+
249
+ return handler(ctx, req)
250
+ }
251
+ }
252
+ }
253
+ ```
254
+
255
+ ### Client Usage
256
+
257
+ ```bash
258
+ # First request
259
+ curl -X POST -H "x-idempotent: abc123" https://api.example.com/v1/orders
260
+
261
+ # Retry with same token (fails with 409)
262
+ curl -X POST -H "x-idempotent: abc123" https://api.example.com/v1/orders
263
+
264
+ # New request with new token
265
+ curl -X POST -H "x-idempotent: xyz789" https://api.example.com/v1/orders
266
+ ```
267
+
268
+ ---
269
+
270
+ ## Data Masking
271
+
272
+ Hide sensitive data in responses and logs:
273
+
274
+ ### Struct Tag Approach
275
+
276
+ ```go
277
+ type User struct {
278
+ Name string `json:"name"`
279
+ Mobile string `json:"mobile" mask:"mobile"` // 138****5678
280
+ Email string `json:"email" mask:"email"` // abc***@example.com
281
+ IDCard string `json:"id_card" mask:"idcard"` // 310***1234
282
+ BankCard string `json:"bank_card" mask:"bankcard"` // 6222 **** 0123
283
+ }
284
+ ```
285
+
286
+ ### Masking Functions
287
+
288
+ ```go
289
+ var defaultRules = map[string]MaskRule{
290
+ "mobile": {
291
+ Pattern: regexp.MustCompile(`^(\d{3})\d{4}(\d{4})$`),
292
+ Replacement: "$1****$2",
293
+ },
294
+ "email": {
295
+ Pattern: regexp.MustCompile(`^(.{3}).*(@.*)$`),
296
+ Replacement: "$1***$2",
297
+ },
298
+ "idcard": {
299
+ Pattern: regexp.MustCompile(`^(.{6}).*(.{4})$`),
300
+ Replacement: "$1********$2",
301
+ },
302
+ "bankcard": {
303
+ Pattern: regexp.MustCompile(`^(\d{4})\d+(\d{4})$`),
304
+ Replacement: "$1 **** **** $2",
305
+ },
306
+ "name": {
307
+ Handler: func(s string) string {
308
+ if len(s) <= 1 {
309
+ return s
310
+ }
311
+ return s[:1] + strings.Repeat("*", len(s)-1)
312
+ },
313
+ },
314
+ }
315
+ ```
316
+
317
+ ### Masker Implementation
318
+
319
+ ```go
320
+ type Masker interface {
321
+ Mask(interface{}) interface{}
322
+ }
323
+
324
+ func (m *DefaultMasker) Mask(data interface{}) interface{} {
325
+ value := reflect.ValueOf(data)
326
+
327
+ if value.Kind() == reflect.Struct {
328
+ result := reflect.New(value.Type()).Elem()
329
+ for i := 0; i < value.NumField(); i++ {
330
+ field := value.Field(i)
331
+ maskTag := value.Type().Field(i).Tag.Get("mask")
332
+
333
+ if maskTag != "" && field.Kind() == reflect.String {
334
+ if rule, ok := m.rules[maskTag]; ok {
335
+ masked := m.applyRule(rule, field.String())
336
+ result.Field(i).SetString(masked)
337
+ }
338
+ } else {
339
+ result.Field(i).Set(field)
340
+ }
341
+ }
342
+ return result.Interface()
343
+ }
344
+ return data
345
+ }
346
+ ```
347
+
348
+ ---
349
+
350
+ ## Log Filtering
351
+
352
+ Filter sensitive data from logs:
353
+
354
+ ```go
355
+ import "github.com/go-kratos/kratos/v2/log"
356
+
357
+ h := log.NewHelper(
358
+ log.NewFilter(logger,
359
+ // Filter by level
360
+ log.FilterLevel(log.LevelError),
361
+
362
+ // Filter by key
363
+ log.FilterKey("password"),
364
+
365
+ // Filter by value
366
+ log.FilterValue("secret"),
367
+
368
+ // Custom filter
369
+ log.FilterFunc(func(level log.Level, keyvals ...interface{}) bool {
370
+ for i := 0; i < len(keyvals); i += 2 {
371
+ if keyvals[i] == "token" {
372
+ keyvals[i+1] = "***MASKED***"
373
+ }
374
+ }
375
+ return false // Return false to continue logging
376
+ }),
377
+ ),
378
+ )
379
+ ```
380
+
381
+ ---
382
+
383
+ ## Header/Context Extraction
384
+
385
+ Get request information in middleware or service:
386
+
387
+ ```go
388
+ import (
389
+ "github.com/go-kratos/kratos/v2/transport"
390
+ "github.com/go-kratos/kratos/v2/transport/http"
391
+ )
392
+
393
+ func extractInfo(ctx context.Context) {
394
+ // Get transport
395
+ if tr, ok := transport.FromServerContext(ctx); ok {
396
+ operation := tr.Operation()
397
+
398
+ // Get HTTP-specific info
399
+ if hr, ok := tr.(*http.Transport); ok {
400
+ method := hr.Request().Method
401
+ path := hr.Request().URL.Path
402
+ userAgent := hr.RequestHeader().Get("User-Agent")
403
+ authorization := hr.RequestHeader().Get("Authorization")
404
+ }
405
+ }
406
+
407
+ // Set response header
408
+ if httpCtx, ok := ctx.(http.Context); ok {
409
+ httpCtx.Response().Header().Set("X-Custom", "value")
410
+ }
411
+ }