File size: 11,757 Bytes
6182c14
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
{
  "entities": {
    "User": {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "title": "User",
      "type": "object",
      "description": "Represents an individual user of the ByteBrief application, managing their uploaded documents and generated summaries. User authentication details are handled by an external system, with only non-sensitive profile information stored here.",
      "properties": {
        "id": {
          "type": "string",
          "description": "Unique identifier for the User entity."
        },
        "email": {
          "type": "string",
          "description": "The primary email address of the user, used for identification within the application.",
          "format": "email"
        },
        "firstName": {
          "type": "string",
          "description": "The first name of the user."
        },
        "lastName": {
          "type": "string",
          "description": "The last name of the user."
        },
        "createdAt": {
          "type": "string",
          "description": "Timestamp indicating when the user account was created.",
          "format": "date-time"
        },
        "updatedAt": {
          "type": "string",
          "description": "Timestamp indicating when the user account was last updated.",
          "format": "date-time"
        }
      },
      "required": [
        "id",
        "email",
        "firstName",
        "lastName",
        "createdAt",
        "updatedAt"
      ]
    },
    "Document": {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "title": "Document",
      "type": "object",
      "description": "Represents a document or file uploaded by a user for summarization, securely stored in a cloud-based storage solution.",
      "properties": {
        "id": {
          "type": "string",
          "description": "Unique identifier for the Document entity."
        },
        "userId": {
          "type": "string",
          "description": "Reference to the User who uploaded this document. (Relationship: User 1:N Document)"
        },
        "fileName": {
          "type": "string",
          "description": "The original name of the file when it was uploaded by the user."
        },
        "cloudStoragePath": {
          "type": "string",
          "description": "The unique path or key identifying the document's location within the cloud storage system (e.g., S3 object key, Blob storage path)."
        },
        "fileType": {
          "type": "string",
          "description": "The MIME type or file extension of the uploaded document (e.g., 'application/pdf', 'text/plain', 'image/jpeg')."
        },
        "fileSize": {
          "type": "number",
          "description": "The size of the document in bytes."
        },
        "uploadDate": {
          "type": "string",
          "description": "Timestamp indicating when the document was uploaded by the user.",
          "format": "date-time"
        },
        "status": {
          "type": "string",
          "description": "The current processing status of the document (e.g., 'uploaded', 'processing', 'summarized', 'failed', 'deleted')."
        },
        "content": {
          "type": "string",
          "description": "The full text content of the document."
        },
        "originalContentHash": {
          "type": "string",
          "description": "A cryptographic hash (e.g., SHA256) of the document's content, useful for integrity checks or duplicate detection."
        }
      },
      "required": [
        "id",
        "userId",
        "fileName",
        "fileType",
        "fileSize",
        "uploadDate",
        "status"
      ]
    },
    "Summary": {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "title": "Summary",
      "type": "object",
      "description": "Represents an AI-generated summary of a specific document, linked to the original document and the owning user.",
      "properties": {
        "id": {
          "type": "string",
          "description": "Unique identifier for the Summary entity."
        },
        "documentId": {
          "type": "string",
          "description": "Reference to the Document for which this summary was generated. (Relationship: Document 1:N Summary)"
        },
        "userId": {
          "type": "string",
          "description": "Reference to the User who owns this summary (derived from the document's owner). (Relationship: User 1:N Summary)"
        },
        "summaryText": {
          "type": "string",
          "description": "The actual text content of the AI-generated summary."
        },
        "summaryLength": {
          "type": "string",
          "description": "Describes the intended length or verbosity of the summary (e.g., 'concise', 'detailed', 'keypoints')."
        },
        "generatedAt": {
          "type": "string",
          "description": "Timestamp indicating when the summary was generated by the AI.",
          "format": "date-time"
        },
        "aiModelUsed": {
          "type": "string",
          "description": "Identifier for the specific AI model or configuration that was used to generate this summary."
        },
        "version": {
          "type": "number",
          "description": "A version number for the summary, useful if multiple summaries are generated or re-generated for the same document over time."
        }
      },
      "required": [
        "id",
        "documentId",
        "userId",
        "summaryText",
        "generatedAt"
      ]
    }
  },
  "auth": {
    "providers": [
      "password"
    ]
  },
  "firestore": {
    "structure": [
      {
        "path": "/users/{userId}",
        "definition": {
          "entityName": "User",
          "schema": {
            "$ref": "#/backend/entities/User"
          },
          "description": "Stores individual user profiles. This collection is owned by the user, and access is restricted to the authenticated user matching 'userId'.",
          "params": [
            {
              "name": "userId",
              "description": "The unique identifier of the user, corresponding to Firebase Authentication UID."
            }
          ]
        }
      },
      {
        "path": "/users/{userId}/documents/{documentId}",
        "definition": {
          "entityName": "Document",
          "schema": {
            "$ref": "#/backend/entities/Document"
          },
          "description": "A subcollection under a specific user, containing metadata for documents uploaded by that user. Each document includes the denormalized 'userId' field for authorization independence.",
          "params": [
            {
              "name": "userId",
              "description": "The unique identifier of the user who owns this document, corresponding to Firebase Authentication UID."
            },
            {
              "name": "documentId",
              "description": "The unique identifier for the specific document."
            }
          ]
        }
      },
      {
        "path": "/users/{userId}/documents/{documentId}/summaries/{summaryId}",
        "definition": {
          "entityName": "Summary",
          "schema": {
            "$ref": "#/backend/entities/Summary"
          },
          "description": "A nested subcollection under a specific document, containing AI-generated summaries for that document. Each summary includes denormalized 'userId' and 'documentId' fields for authorization independence.",
          "params": [
            {
              "name": "userId",
              "description": "The unique identifier of the user who owns this summary, corresponding to Firebase Authentication UID."
            },
            {
              "name": "documentId",
              "description": "The unique identifier of the document this summary belongs to."
            },
            {
              "name": "summaryId",
              "description": "The unique identifier for the specific summary."
            }
          ]
        }
      }
    ],
    "reasoning": "The Firestore structure for ByteBrief prioritizes secure, scalable, and debuggable access to user-owned documents and their summaries. It adheres strictly to the core design principles, especially Authorization Independence and the enforcement of Query-Authentic Paths (QAPs).\n\n1.  **Users Collection (`/users/{userId}`):** This top-level collection stores individual user profiles. Access is strictly private, meaning only the authenticated user (`request.auth.uid`) can read or write their own user document. This path establishes path-based ownership, making user profile rules straightforward (e.g., `allow read, write: if request.auth.uid == userId;`).\n\n2.  **Documents Subcollection (`/users/{userId}/documents/{documentId}`):** Each user's uploaded documents are stored as a subcollection under their respective user profile. This hierarchical structure inherently links a document to its owner. Critical for **Authorization Independence**, each `Document` document will explicitly contain a `userId` field (denormalized from the parent user). This allows security rules to verify ownership directly within the document (`resource.data.userId`) without requiring a `get()` call to the parent user document. This is crucial for enabling atomic operations and simplifying rule logic. This structure supports QAPs by allowing clients to query for documents belonging to `request.auth.uid` within the specific user's path, and rules can directly validate `request.auth.uid == userId` (the path wildcard).\n\n3.  **Summaries Subcollection (`/users/{userId}/documents/{documentId}/summaries/{summaryId}`):** AI-generated summaries are stored as a further nested subcollection, under the specific document they relate to. This continues the strong hierarchical ownership model. For **Authorization Independence**, each `Summary` document will explicitly contain both `userId` and `documentId` fields. This denormalization allows security rules to verify both the user's ownership and the summary's linkage to the correct document without any `get()` operations on parent documents. This enables `allow read, write: if request.auth.uid == userId;` to be applied efficiently and securely. QAPs are supported by the nested path structure, allowing specific user and document-scoped queries where rules can directly check `request.auth.uid == userId` (the path wildcard).\n\n**Authorization Independence via Denormalization:** This design achieves Authorization Independence by explicitly embedding `userId` in every `Document` document and both `userId` and `documentId` in every `Summary` document. This means that any security rule for a `Document` or `Summary` can evaluate ownership (`request.auth.uid == resource.data.userId`) directly on the document being accessed, without needing to perform `get()` operations on parent collections to determine the owner. This is fundamental for robust and efficient security rules, particularly in transactions and batched writes.\n\n**Support for QAPs (Query-Authentic Paths):** The entire structure is designed around path-based ownership (`/users/{userId}/...`). This inherently makes `list` operations secure and efficient. When a user queries for their documents or summaries, they will naturally be querying within their specific `/users/{request.auth.uid}/` path. The security rules can then simply verify that `request.auth.uid` matches the `{userId}` wildcard in the path, ensuring that users can only query their own data. The explicit `userId` and `documentId` fields in the documents further reinforce this, allowing for rules like `allow read: if request.auth.uid == userId;` which are naturally query-authentic."
  }
}